상태를 저장하고 관리하는 객체는 하나(권장), 그 상태를 사용하는 객체는 여러 개가 있을 수 있다.
--> 옵저버 패턴에서 상태를 관리하는 객체는 서브젝트, 상태에 의존하는 객체는 옵저버 ==> 1:다 관계
1번과 연관된 내용이다
데이터를 제어하는 주체는 하나, 나머지는 데이터가 바뀌었을 때 제어자가 갱신해주도록 기다리는 의존자들
이런 식으로 데이터를 사용하면 여러 객체가 데이터를 제어하는 방식보다 더 깔끔한 객체지향 디자인을 만들 수 있다.
느슨한 결합: 서로 알긴 아는데.. 필요한(약속한) 부분만 아는 사이
--> 서브젝트는 옵저버의 구현 클래스가(반대도 성립) 옵저버 인터페이스를 구현한다는 정보만 알고 있다.
각자의 인터페이스를 구현한다는 조건만 만족하면, 서로 어떻게 바뀌고 사용되든 서로 상관하지 않고 독립적으로 사용될 수 있다
디자인 원칙: 서로 상호작용 하는 사이에는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
--> 느슨한 결합에서 이야기했듯 느슨할 수록 상호의존성이 적어서 변경에 유연할 수 있다.
--> 인터페이스에 의존하기에 변경에 유연하다의 의미를 아직 잘 모르겠다. 내가 생각하기에 인터페이스에 의존했을 때 장점은
서로 다른 타입의 구현 클래스들을 하나의 옵저버 타입으로 취급할 수 있다는 것이다. 다형성, 동적 유연성이라고 해야 할까?
구성 형식이 달라도 같은 인터페이스를 구현해야만 서브젝트에서 업데이트 해줄 수 있다.
GUI 프레임워크에서 옵저버 패턴이 많이 쓰인다 => 안드로이드에서 아주 많이 쓰임: 리스너, 라이브데이터, 플로우 등
인터페이스를 사용, 인터페이스만 참조하게 만들면 결합이 느슨해진다.
디자인 원칙: 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다. (OCP)
모든 부분에 대해 객체 지향 디자인을 하기보다 (WHY? 시간이 충분치 않다, 필요 없는 상황도 있다), 자주 변경되는 부분에 대해
중점적으로 살펴보고 적용하자.
=> 확장할 부분을 선택할 때는 주의를 기울여야 한다. 무조건 OCP를 적용하는 것은 시간 낭비가 될 수 있다. 결과적으로 불필요하게
복잡하고 이해하기 힘든 코드만 만드는 부작용을 낳을 수 있다.
데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행할 수 있습니다.
데코레이터는 자신이 장식하는 객체와 같은 인터페이스(추상클래스)를 구현한다.
-> 여기서 상속의 목적은 자신이 감싸는(꾸미는) 객체와 같은 타입을 물려받는 것이지, 행동을 물려받는 것이 아니다.
그리고 거기에 더해 새로운 기능을 덧붙이기 위해 사용한다.
데코레이터 패턴의 역할(목적)
1) 객체에 '동적'으로 요소를 '추가'할 수 있다. (데코레이터를 조합한다)
--> 데코레이터를 파고 들어서 작업을 해야 한다면 이 패턴의 의도에 어긋나는 것
2) 서브 클래스를 추가하는 것으로 유연하게 기능을 확장할 수 있다.
합성 + 위임 -> 데코레이터 패턴에서 장식당하는 객체(합성)의 메서드 호출(위임) + 자신의 구현을 추가
상속 + 위임보다 안전하고 변경에 유연하다
대신 구현 클래스의 종류에 따라 다른 구현이 필요한 경우에는 데코레이터 패턴을 사용할 수 없다.
왜냐면 데코레이터로 감싸서 타입을 알 수가 없기 때문이다.
데코레이터 패턴은 일반적으로 팩토리, 빌더 패턴과 함께 사용하여 더 추상화한다. (곧 배움)
단점:
1) 잡다한 클래스가 많아진다. -> 패턴을 잘 모르는 개발자는 이해하기 어렵다 but 이해하고 나면 데코레이터로 감싸서 원하는
행동을 구현할 수 있다.
2) 객체를 생성할 때 초기화 코드가 복잡해진다 -> 구성 요소 객체들을 모두 생성해야 하기 때문인듯 -> 팩토리, 빌더 패턴으로 해결
클라이언트는 자신이 의존하는 객체가 데코레이터인지 모른다