오늘은 2장의 내용을 정리하려고 한다.
앞서 진행한 1장과 같은 방법으로 정리할 것이다.
이상한 나라의 객체
객체지향 패러다임은 지식을 추상화하고 추상화한 지식을 객체 안에 캡슐화함으로써 실세계 문제에 내재된 복잡성을 관리하려고 한다.
객체를 발견하고 창조하는 것은 지식과 행동을 구조화하는 문제다. - 레베카 워프스브룩
객체지향과 인지능력
소프트웨어 세계는 인간이 인지할 수 있는 다양한 객체들이 모여 이루어져 있다는 믿음으로 시작된다.
객체지향은 현실 세계를 모방하는 것이 아닌 창조하는 일이다.
현실세계와는 다르게 사람의 손길 없이 동작할 수 있다. (스스로 움직이는 자동차, 스스로 불을 껐다 키는 전등)
객체, 그리고 이상한 나라
엘리스는 토끼를 따라가 굴속으로 들어가며 작은 문을 발견한다. 이후 이 문들 통과하기 위해 본인의 몸을 작게 만드는
방법을 찾게 된다. 여러 방법(음료수를 마시면 키가 작아지며, 케이크를 먹으면 몸이 커진다)을 통해 몸의 크기를 조절하여 문을 통과할 수 있게 된다.
엘리스 객체
- 엘리스는 상태를 가지며 상태는 변경이 가능하다. - 키가 자유롭게 변할 수 있다.
- 엘리스의 상태를 변경시키는 것은 엘리스의 행동이다. - 음료수를 마시거나, 케이크를 먹으면 키가 변한다.
- 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있다. - 어떤 행동에 의해 키가 변하게 된다.
- 행동의 순서가 결과에 영향을 미친다. - 문을 통과하기 위해 음료수와 케이크를 먹는 행동에서 선행이 필요하다.
- 엘리스는 어떤 상태여도 유일하게 식별할 수 있다. - 키가 달라져도 엘리스는 엘리스 그 자체이다.
객체, 그리고 소프트웨어 나라
객체
객체란 식별 가능한 개체 또는 사물이다. 객체는 쉽게 생각할 수 있는 컴퓨터와 같은 구체적인 사물일 수 도있고,
시간과 같은 추상적인 개념일 수 도 있다.
객체는 구별 가능한 식별자, 특징적인 행동, 변경가능한 상태로 이루어져 있다.
상태
상태는 특정 시점에 객체가 가지고 있는 정보의 집합으로 객체의 구조적 특징을 표현한다.
객체의 상태는 객체에 존재하는 정적인 프로퍼티와 동적인 프로퍼티 값으로 구성된다.
객체의 프로퍼티는 단순한 값과 다른 객체를 참조하는 링크로 구분할 수 있다.
- 어떤 행동의 결과는 과거에 어떤 행동들이 일어났었느냐에 의존적이다.
- 인간은 행동의 과정과 결과를 단순하게 기술하기 위해 상태를 사용한다.
- 상태를 이용하면 과거의 모든 행동 이력을 몰라도 행동의 결과를 쉽게 예측할 수 있다.
- 객체는 스스로의 행동에 의해서만 상태가 변경되는 것을 보장함으로써 객체의 자율성을 유지한다.
행동
행동이란 외부의 요청 또는 수신된 메시지에 응답하기 위해 동작하고 반응하는 활동이다.
행동의 결과로 객체는 자신의 상태를 변경하거나 다른 객체에게 메시지를 전달할 수 있다.
객체는 행동을 통해 다른 객체와의 협력에 참여하므로 행동은 외부에 가시적이어야 한다.
- 객체의 행동은 객체의 상태를 변경시키지만 행동의 결과는 객체의 상태에 의존적이다.
- 행동은 객체의 상태에 영향을 받으며, 상태를 변경시킨다.
- 객체와 객체는 메시지를 통해서만 의사소통을 한다.
- 행동은 상태에 어떤 방식으로 의존하는가 와 어떻게 상태를 변화시키는지의 관점에서 서술가능하다.
식별자
식별자란 어떤 객체를 다른 객체와 구분하는 데 사용하는 객체의 프로퍼티이다.
값은 식별자를 가지지 않기 때문에 상태를 이용한 동등성 검사를 통해 두 인스턴스를 비교해야 한다.
객체는 상태가 변경될 수 있기 때문에 식별자를 이용한 동일성 검사를 통해 두 인스턴스를 비교할 수 있다.
- 객체와 값을 지칭하는 별도의 용어 참조객체, 엔티티 -> 식별자
- 값자체 -> 식별자를 가지지 않는 값
정리
- 객체는 상태를 가지며 상태는 행동에 의해 변경가능
- 객체는 식별자가 존재하기에 유일하게 식별가능
- 객체지향은 모방이 아닌 새로운 세계를 창조하는 것
이를 적용해 간단하게 코드를 작성해 보았다.
우선 Human이라는 인터페이스를 생성하고, 엘리스라는 객체로 구체화하였다.
public interface Human {
void eat(Food food);
}
public class Alice implements Human{
private static Alice alice;
private int height;
private Location location;
public static Alice getInstance(int height, Location location) {
if (alice == null) {
alice = new Alice(height, location);
}
return alice;
}
private Alice(int height, Location location) {
this.height = height;
this.location = location;
}
@Override
public void eat(Food food) {
food.reduce();
if (food.isPlus()){
height += 10;
return;
}
height -= 10;
}
public void move () {
location = Location.정원;
}
public int getHeight() {
return height;
}
public Location getLocation() {
return location;
}
}
싱글톤을 사용하여 만들었다. 사실 인터페이스의 역할이 여기에서는 필요가 없기도 하였다.
Food를 인터페이스로 만들고 이를 통해 키를 증가시키는 음식과 감소시키는 음식을 만들었다.
public interface Food {
boolean isPlus();
int getCount();
void reduce();
}
public class PlusFood implements Food{
private static PlusFood plusFood;
private int count;
public static PlusFood getInstance(int count) {
if (plusFood == null) {
plusFood = new PlusFood(count);
}
return plusFood;
}
private PlusFood(int count) {
this.count = count;
}
@Override
public boolean isPlus() {
return true;
}
@Override
public void reduce() {
validation();
count--;
}
@Override
public int getCount() {
return count;
}
private void validation() {
if (count == 0) {
throw new IllegalStateException("전부 먹었습니다.");
}
}
}
public class MinusFood implements Food{
private static MinusFood minusFood;
private int count;
public static MinusFood getInstance(int count) {
if (minusFood == null) {
minusFood = new MinusFood(count);
}
return minusFood;
}
private MinusFood(int count) {
this.count = count;
}
@Override
public boolean isPlus() {
return false;
}
@Override
public void reduce() {
validation();
count--;
}
@Override
public int getCount() {
return count;
}
private void validation() {
if (count == 0) {
throw new IllegalStateException("전부 먹었습니다.");
}
}
}
Food라는 인터페이스를 둘 다 구현하였다. 따라서 엘리스 객체에서 eat(Food food)에 두 개의 구현체 모두 사용될 수 있다.
public enum Location {
굴, 정원
}
위치는 공통 속성이기에 enum을 사용하였다.
public class Door {
int height;
public Door(int height) {
this.height = height;
}
public boolean isPass(int height) {
if (this.height >= height) {
return true;
}
return false;
}
}
문의 경우 문을 열고 지나갈 수 있는지에 대한 로직을 담고 있다.
public class Story {
public static void main(String[] args) {
Alice alice = Alice.getInstance(160, Location.굴);
Door door = new Door(100);
Food minusFood = MinusFood.getInstance(10);
while (true) {
print(alice, minusFood);
if (door.isPass(alice.getHeight())){
alice.move();
System.out.println(alice.getLocation());
break;
}
alice.eat(minusFood);
}
}
private static void print(Alice alice, Food minus) {
System.out.println(alice.getHeight());
System.out.println(alice.getLocation());
System.out.println(minus.getCount());
}
}
엘리스 객체의 키가 문의 높이보다 클 경우 키가 작아지는 음식을 먹고, 더 작아진다면 문을 통과하는 로직이다.
구현 코드에 대한 피드백
우선 디테일한 피드백은 Human의 인터페이스의 역할이 필요 없다는 것이다. 실제 인터페이스를 사용하지만 구현체가 1개뿐이기에
위 코드에서는 실질적으로 의미가 없다. 하지만 확장가능성에 대해 열려있다는 장점은 분명히 가지고 있다.
Food인터페이스를 구현하는 FoodImpl을 만들고 이를 상속을 통해 PlusFood나 MinusFood를 만들면 분명 코드의 재사용성이
좋아질 것이라는 피드백이다. 이는 분명 맞다고 생각한다. 지금 현재 나의 코드를 보면 코드 중복이 일어나고 있다. 이 부분을 더 신경 써야겠다.
또한 enum을 더 잘 사용하는 법을 학습해야 한다. 예전에 enum에 대해 스스로 학습하고 어느 정도 활용하는 방법을 익혔으나,
현재 잘 사용하지 않아 제대로 된 사용법을 알지 못하고 이를 더욱 잘 활용할 수 있음에도 그러지 못하였다.
이전에 enum을 학습한 적이 있기에 더 빠르고 쉽게 학습할 수 있을 것이다.
책을 읽고 어떤 것을 의미하고 무엇을 강조하고 싶어 하는지에 대해 쉽게 파악할 수 있었다.
하지만 이를 단순하게 이해하는 것은 쉽지만 코드에 적용하는 것은 결코 쉽지 않다고 깨달았다.
의식적으로 객체를 지향하고, 더 학습하자.
'좋은 개발자가 되기' 카테고리의 다른 글
객체지향의 사실과 오해(4장) (0) | 2023.04.25 |
---|---|
객체지향의 사실과 오해(3장) (2) | 2023.04.17 |
객체지향 생활 체조 원칙 (0) | 2023.04.16 |
객체지향의 사실과 오해 (1장) (0) | 2023.04.05 |
자바 코드 컨벤션 (2) | 2023.03.29 |