사례 연구: 기상관측기

저가형 소프트웨어 님버스

  • 1.0 : 경쟁사를 대응하고자 하는 저가형 솔루션
    • 만들수록 적자가 나는 구조 (비싼 하드웨어)
    • 현재 시간이 없어서 고급하드웨어를 사용
  • 2.0 : 이후 경쟁사를 따돌리는 완성형 저가형 솔루션
    • 1.0은 비싼 하드웨어 이므로 2.0으로 빨리 변환해야함

님버스-LC 소프트웨어 설계

  • 하드웨어 독립적 아키텍처
  • 테스트
    • mocking을 통한 단위테스트
  • 스케줄러
    • 정기적으로 기상관측 정보를 계측
  • 기압동향
  • UI 독립적

스케줄러 생각해 보기

  1. 정기적으로 계측하기 위해서 스케줄러가 필요하다.
  2. 스케줄러가 UI와 모든 감지기와 연결되어 있다
    1. 이것은 감시기가 추가되거나 UI에 의해서 변경이 닫혀있지 않게 된다 - OCP 위반
  3. UI와의 결합은 옵저버 패턴으로 해결 - p.461 ~ 462
  4. 감지기와의 결합은 Listener로 해결(일종의 콜백 개념) - p.463
    1. 감지기 자신이 풀링 주기를 알아야 한다!

감지기(Sensor)

감지기 클래스 다이어그램

1 Cycle

  1. 알람시계(스케줄러)가 감지기를 깨운다.
  2. 감지기는 check() 메소드를 통해서 시작한다.
    1. 먼저 계측값을 읽어온다.
    2. 읽어온 계측값을 옵저버를 통해서 UI에 발행

하드웨어 다형성

  • TemperatureSensor#check() : 템플릿 메소드
  • Nimbus1TemperatureSensor#read() : 템플릿의 다형성 메소드
    • 즉 다른 감지기들도 read() 만 재정의 하면 된다.

감지기 개선

감지기 브릿지 패턴 적용

브릿지 패턴 적용

감지기 팩토리 패턴 적용

팩토리 패턴 적용

스케줄러 개선

  • AlarmClock도 감지기 개선과 유사하게 브릿지와 팩토리를 바탕으로 리팩토링 한다.
  • ISSUE 자바에서는 객체의 이름(String)으로 객체를 생성할 수 있다.
    • 타입안정성이 없다. - 아래코드 참조
public static void main(String[] args) {
    Class tkClass = Class.forName(args[0]);
    StationToolKit st = (StationToolKit) tkClass.newInstance();
}

패키징

  • 이미 전에 배웠다시피 클래스를 설계하고 나서 패키지를 설계한다 : 상향식
  1. UI가 다른 클래스들과 같은 패키지라서 분리하는 것이 좋을 것 같다. - p.470
  2. ISSUE 그랬더니 순환참조(?)가 생겨버렸다. - p.472
  3. 순환참조를 DIP를 이용해서 해결했다.
    1. WeatherStationComponent를 만들어서 해결 - p.473

영속화

API

interface PersistentImpl {
    void store(String name, Serializable obj);
    Object retrieve(String name);
    List directory(String regExp);
}

24시간 기록

일일(0 ~ 24시까지의) 최솟값과 최댓값

  1. 감지기를 통해 온도의 변화가 있으면 최솟값, 최댓값을 다시 저장한다. : currentReading()
  2. 00시가 되면 현재 온도를 조회한 수 새로운 일일 최솟값, 최댓값을 생성한다. : newDay()

p. 477 ~ 478

하지만

정책 (최솟값과 최댓값을 구하기)과 영속성 관심사가 섞여 있다.

  • 이럴 때 해결방안은 역시나 프록시 패턴을 이용함

프록시 패턴을 이용한 정책과 영속성 분리

프록시 패턴을 이용한 정책과 영속성 분리

  • HiLoDataDbProxy : 영속성 담당
  • HiLoDataImpl : 정책 담당

팩토리와 초기화

  • TemperatureHiLo 입장에서는 HiLoData에만 의존하고 싶다.
  • 하지만 누군가는 HiLoDataImpl, HiLoDataDbProxy를 생성해야한다.
  • 이러한 것을 팩토리를 통해서 캡슐화한다.
  • 팩토리를 사용하는 것은 다형성을 포함해서 복잡한 생성이나 구상체를 캡슐화 할 때도 유용하다.
  • 여기에서는 DataToolKit라는 추상팩토리를 통해서 해결한다. - p.483

영속성으로 인한 패키지 구조 변경

  • 영속성 레이어가 추가됨에 따라 패키지 구조를 다시 변경해야 한다.
  • 패키지는 어플리케이션이 확장될 수록 계속 바뀔 수 있다.

그럼 영속성 팩토리는 누가 생성?

  • new persistence.DataToolKitImpl() -> persistence.Scope.init() -> main()
  • 하지만 위 케이스에서 일부 의존성 때문에 또 다른 팩토리를 생성하고 패키지를 분리하는 것은 오버엔지니어링인 것 같다. - p.486 이것이 정말로 필요한가?
    • ISSUE 현실적으로 요구사항에 맞게 일부 강결합은 인정해야할 것 같다.
  • 애초에 이런 고민은 IoC컨테이너(ex: Spring 프레임워크)를 이용하면 쉽게 해결할 수 있다.

결론

  • 해당 장을 보고 실제 설계 & 구현을 이루는 예시를 통해서 practice를 얻기를 바란다.

컴포지트 패턴

부분과 전체의 계층을 표현하기 위해 객체들을 모아 트리 구조로 구성. 사용자로 하여금 개별 객체와 복합 객체를 동일하게 다룰 수 있도록 하는 패턴

Class Diagram

컴포지트 패턴 클래스 다이어그램

  • ShapeComponent 이다.
  • Circle, Square의 경우에는 더 이상 복합이 불가능하므로 Leaf 이다.
  • CompositeShapeComposite(복합체) 이다. - 위임자

참고

Source

interface Shape {
    void draw();
}

class CompositeShapte implements Shape {
    private List<Shape> shapes = new ArrayList<>();
    public void (Shape s) {
        shapes.add(s);
    }
    public void draw() {
        shapes.forEach(Shape::draw);
    }
}

결론

  • 1:N 관계를 1:1 관계로 단순화 시킨다.
  • 다수성을 가지는가? (1:N 관계를 1:1관계로 변경할 수 있는가?)

옵저버 패턴

그 전에

Worst Practice

  1. 패턴을 먼저 생각하고 그것을 해결책으로 사용한다.
  2. 패턴은 구조적인 문제해결 방법인데, 그것을 문제에 대입한다?? : 주객전도

Best Practice

  1. 문제에 맞는 적절하고 단순한 리팩토링
  2. 그러다 보면 특정 패턴과 유사해진다.
  3. 그러면 본격적으로 해당 패턴으로 리팩토링 한다.

옵저버 패턴이란?

객체 사이에 1:N 관계를 정의하고 어떤 객체의 상태가 변할 때 그 객체에 묵시적인 의존성(추상적인 결합도)을 가진 다른 객체들이 그 변화를 통지 받고 자동으로 갱신될 수 있게 만듦

  • Publish-Subscribe

옵저버 패턴 클래스 다이어그램

옵저버 패턴 클래스다이어그램

옵저버 패턴 시퀀스 다이어그램

옵저버 패턴 시퀀스다이어그램

  1. 주체(subject)의 상태 변경이 발생 : subject.setState()
    1. 여기서는 감시자(observer)가 호출했지만, 다른 객체가 주체의 상태 변경을 호출할 수 있음
  2. 주체는 변경을 통보함 : this.notify()
  3. (감시자들을 순회하면서) 감시자에게 변경을 통보 : observer.update()
  4. 감시자는 주체에게서 변경된 상태를 얻어옴 : subject.getState()
    1. 그리고 주체의 상태를 감시자 자신의 상태와 일치시킴 : `observerState = subject.getState();

장점

  • 객체 간 협력을 위해서 상호 양방향 연관관계가 필요한 상황(주로 순환참조가 발생할 시)에서 단방향 참조로 변경하면서 결합도를 낮출 수 있음
  • 객체 간 일관성을 유지하면서 (동기화가 하지 않으면서) 결합도를 낮출 수 있음

대표적 예시

  • MVC의 Model과 View

Pull vs Push

Pull-model

감시자가 주체에게서 데이터를 끌어오는(pull) 방식

  • 주체가 감시자를 전혀 몰라도 됨
  • 감시자가 무엇이 변했는지 항상 확인해야 하므로 비효율적일 수 있음

Push-model

옵저버 패턴 Push-model

주체가 감시자에게 자신의 변경에 대한 상세한 정보를 밀어내는(push) 방식

  • 어떤 정보가 변하는지 알 수 있음
  • 주체가 감시자의 요청이 무엇인지 알아야 함
  • Pull 모델에 비해 재사용성이 떨어지며 주체의 어떤 정보가 변하는 것에 대한 의존성이 생김
    • 어떤 정보가 변하는 것 : subject.notify, observer.update 메소드의 파라미터

옵저버와 OOD

  • OCP : 주체 변경 없이(closed) 새로운 관찰자 추가(open) 가능
  • LSP
    • SubjectConcreteSubject
    • ObserverConcreteObserver
  • DIP : ConcreteSubject -> Observer
    • ISSUE 하지만 ConcreteObserverConcreteSubject에 의존한다.
  • ISP : ISSUE 이건 좀 어거지 아닌가?

추상서버 패턴

추상 서버 패턴

Switchable 이 추상서버가 된다.

인터페이스는 누가 소유하는가?

  • 클라이언트가 소유해야한다.
  • 인터페이스는 구상체가 아니라 클라이언트를 위한 추상화이다.
  • ISP를 상기하자

어댑터 패턴

  • 만약 Light가 서드파티라면 상속할 수 없다.
  • 서드파티 등의 외부 의존을 줄이고 인터페이스 호환을 위해서 어댑터가 필요하다.
    • 우리가 제어할 수 없는 라이브러리를 어댑터로 감싸자

어댑터 패턴

공짜 점심은 없다.

  • ISSUE 어댑터 사용 비용이 크다?
    • 과연?
  • 추상서버로 대부분 해결가능하면 필요하면 어댑터를 사용하자.

클래스 형태의 어댑터

어댑터 패턴 - 클래스 타입

  • LightAdapterLight가 상속으로 인해 강한 결합이 생긴다.
  • 개인적으로 객체 형태의(합성 기반) 어댑터를 추천한다.

브리지 패턴

브릿지 패턴

  • 추상부(Abstraction)와 구현부(Implementor) 를 분리
  • 관심사 분리가 더 깔끔해 진다.
  • 그 만큼 코드량이 많아지고 복잡해진다.

프록시 패턴

프록시 패턴

프록시 패턴 클래스 다이어그램

프록시 패턴 시퀀스 다이어그램

프록시 패턴 시쿼스 다이어그램

관계 프록시 적용하기

ORM proxy

출처 : Advanced Hibernate: Proxy Pitfalls

ORM 프레임워크들이 실제로 이러한 방식으로 구현되어 있다.

  • 연관관계가 맺어 있는 Entity를 항상 모두 조회할 순 없다.
  • 위와 같이 연관관계에 있는 Entity(boss)를 프록시를 통해서 지연로딩할 수 있게 한다.

프록시 요약

프록시의 가장 큰 장점은 도메인과 기술(인프라)사이의 관심사 분리이다.

천국으로의 계단 패턴

  • 너무 상속을 기반으로 재사용 하는 것 같다.
  • 이런 경우에는 LSP를 위반할 가능성이 크기 때문에 주의를 요한다.

패키지 설계의 원칙

패키지 응집도(cohesion)와 결합도(coupling)에 대한 원칙을 알아보자.

패키지

  • 모듈의 구체화
  • 클래스 보다 더 큰 무엇
    • 클래스 보다 더 큰 추상화 레벨 지닌다.
    • 구체적으로 클래스의 묶음

응집도 : 재사용 릴리즈 등가 원칙(REP)

재사용 단위가 릴리즈의 단위다.

패키지의 모든 클래스가 재사용 가능해야한다.

재사용 릴리즈의 조건

  1. 통보
  2. 안정성
  3. 지원

응집도 : 공통 재사용 원칙(CRP)

패키지 안의 클래스들은 함께 재사용되어야 한다. 어떤 패키지의 클래스 하나를 재사용한다면 나머지도 모두 재사용한다.

  • 연관된 클래스들 끼리 묶어서 군집(패키지)을 이루게 한다.
  • 연관성이 낮으면 같이 묶지 않는다.

응집도 : 공통 폐쇄 원칙(CCP)

같은 패키지 안의 클래스들은 동일한 종류의 변화에는 모두 폐쇄적이어야 한다. 패키지에 어떤 변화가 영향을 미친다면 그 변화는 그 패키지의 모든 클래스에 영향을 미쳐야 하고 다른 패키지에는 영향을 미치지 않아야 한다.

  • 패키지 SRP
  • ISSUE 대부분의 애플리케이션에서 재사용성 보다는 유지보수성이 더 중요하다? - p.326

CCP vs CRP

  • CCP를 확대하면 CRP가 축소 : 전체 패키지 수가 줄어듬 : 개발 용이성
  • CRP를 확대하면 CCP가 축소 : 전체 패키지 수가 늘어남 : 재사용성
  • 서로 상충
  • ISSUE 따라서 패키지 구성은 개발 용이성 -> 재사용성 으로 옮겨가면서 진화한다. - p.326, p.333
    • 이것이 역전이 되는 경우도 있다.

안정성 : 의존 관계 비순환 법칙(ADP)

패키지 의존성 그래프에서 순환을 허용하지 말라.

주간빌드

  • 주 4일 개발 + 주 1일 통합
  • 프로젝트의 복잡도가 커짐에 따라 통합 비용이 점점 더 커지게 되는 악순환

Worst Practice

의존관계 순환을 없애기

Best Practice

의존관계 순환이 생기는 경우

의존관계 순환이 생기는 패키지다이어그램

  • 릴리즈 시 서버 다운을 시키고 한 번에 모든 패키지의 버전을 다 올려서 배포
    • 실질적으로 하나의 큰 패키지 상태
  • 테스트 시 전체 빌드
  • 롤백 시도 마찬가지 - 전체 롤백

왠지 모르게 MSA와 연결이 된다.

순환을 끊기

DIP 적용

DIP 적용

RMI의 그것과 유사하다.

순환을 끊는 공통 패키지 생성

순환을 끊는 공통 패키지 생성

하향식 설계

  • ISSUE 패키지 구조는 하향식으로 설계할 수 없다?? - p.333
    • 시스템을 설계할 때 초기에 나오는 것 가운데 패키지 구조가 포함되지 않는다.
    • 아! 오히려 구체화된 class 등을 만들면서 패키지 구조를 만들어가는 상향식을 말하는 것 이다.
  • 패키지 구조를 먼저 설계하면 실패할 가능성이 크다.
  • 패키지 의존 관계 구조는 시스템의 논리적 설계와 함께 진화해야 한다.

안정성 : 안정된 의존 관계 원칙(SDP)

의존은 안정적인 쪽으로 향해야 한다.

  • SDP가 적용된 패키지는 쉽게 변화도록 설계가 되어 있어서 변경되리라 예상할 수 있다.

안정성

  • 안정성은 변화를 만들기 위해 필요한 일의 양과 관련되어 있다.

    • 세워진 동전을 넘어뜨리려면 일의 양이 적기 때문에 불안정적
    • 탁자를 넘어뜨리려면 상당한 노력이 필요하므로 안정적

X : 안정적인 패키지

X : 안정적인 패키지 : 독립적인 X

Y : 불안정한 패키지

Y : 불안정한 패키지 : 의존적인 Y

모든 패키지가 안정적일 필요는 없다.

  • 만약 모든 패키지가 안정적이라면 시스템은 변경할 수 없게 될 것이다.
  • 공통패키지를 이용 불안정성을 유지해서 확장가능하게 한다.

안정성 : 안정된 추상화 원칙(SAP)

패키지는 자신이 안정적인 만큼 추상적이기도 해야 한다

  • 안정성과 추상성 사이의 관계를 정한다.
    • 안정성 : 확장불가능
    • 추상성 : 확장가능
  • 추상클래스(인터페이스)를 통해서 추상성과 안정성 사이의 균형을 확보
  • 패키지의 성격에 따라 적절한 추상성을 확보해야한다.

stable-abstractions principle

주계열 (붉은색) - A:추상성, I:불안정성

*출처 : Alternative distance function for Stable Abstractions Principle

  • 0,0 : 안정적이고 구체적 : 고통의 지역(Zone of Pain)
    • ex) DB, StringUtils
  • 1,1 : 의존성이 없고(불안정적) 추상적 : 쓸모없는 지역(Zone of Uselessness)
  • 1,0 ~ 0, 1 붉은색 직선(주계열) : 너무 추상적이지도 않고 너무 불안정적 하지도 않음
    • 주계열 양 끝점이 이상적인 위치

팩토리 패턴

DIP 위반

  • 사실 new 키워드를 사용하기만 하면 DIP 위반
  • 필요하면 DIP는 위반하는 것이 맞다.

팩토리 패턴으로 리팩토링

Before

팩토리 적용 전

After

팩토리 적용 후

주의사항

만약 client 에서 new ShapeFactoryImpl()를 하게되면 역시나 DIP를 위반하기 때문에 미리 ShapeFactory 를 의존성 주입 받아야 한다.

의존 관계 순환

interface ShapeFactory {
    makeSquare()
    makeCricle()
}
  • Shape의 파생형(Inheritance type)마다 메소드가 하나가 있다.
  • Shape의 파생형이 하나 추가될 때 마다 의존하게 되는 의존 관계 순환이 생갤 수 있다.

개선

class ShapeFactoryImpl implements ShapeFactory {
    public Shape make(String shapeName) throws Exception {
        if (shapeName.equals("Circle"))
            return new Circle();
        else if (shapeName.equals("Square"))
            return new Square();
        else
            throw new Exception("ShapeFactory cannot crate " + shapeName);
    }
}

하지만

  • OCP 위반
  • 타입안정성(type-safty) 없음

추가개선

interface ShapeInstantiatable {
    Shape newInstance();
}
enum ShapeType implements ShapeInstantiatable {
    SQUARE {
        @Override
        public Shape newInstance() {
            return new Square();
        }
    },
    CIRCLE {
        @Override
        public Shape newInstance() {
            return new Circle();
        }
    };
}
class ShapeFactoryImpl implements ShapeFactory {
    public Shape make(@NonNull ShapeType shapeType) throws Exception {
        return shapeType.newInstance();
    }
}

대체할 수 있는 팩토리

다형적인 팩토리

  • AbstractMemberFactory
    • DbMemberFactory
    • ApiMemberFactory
    • XmlMemberFactory

결론

  • 모든 관계마다 DIP를 적용하기 위해 팩토리를 적용했다간 Interface + Factory Hell에 빠질 것이다.
  • 팩토리 패턴을 사용하면 인터페이스에만 의존하면서도 구체적 객체들을 만들 수 있으므로, 구체 클래스의 변경이 잦을 때 이 패턴이 큰 도움이 된다.

급여관리 사례 연구 - 2부

패키지 구조와 표기법

패키지 구조와 표기법

급여 관리 패키지 다이어그램의 한 예 - p.355

공통 폐쇄 원칙(CCP) 적용하기

공통 폐쇄 원칙(CCP) 적용

급여 관리 애플리케이션의 폐쇄된 패키지 계층 구조의 예 - p.357

  • 책임이 없는 패키지 : payrolldatabaseimpl, textparser : 인프라적 요소들
  • 책임이 있는 패키지(독립적인 패키지) : payrolldomain, applicatoin : 도메인적 요소들

ISSUE 세부적인 것들은 중요한 아키텍처(구조)적인 결정에 의존해야지 의존의 대상이 되면 안된다.

재사용 릴리즈 등가 원칙(REP) 적용하기

비슷한 류의 다른 어플리케이션을 구축할 시

  • 구체적 요소는 재사용 불가능 : 정책이 다름
    • classifications, methods, schedules, affiliations
  • 추상적 요소는 재사용 가능 : 시스템은 비슷함
    • payrolldomain, application, payrolldatabase
    • payrolldatabaseimpl 도 어쩌면 재사용 가능

급여 관리 애플리케이션의 폐쇄된 패키지 계층 구조의 예 - p.357 는 나름 좋은 패키지 구조이나 CRP 원칙을 제대로 지키지 않았다.

  1. paymentdomain 는 재사용 최소 단위가 아니다.
  2. Transaction을 분리해야함

개정된급여관리클래스다이어그램

p.361

  • 이렇게 하므로써 설계에 추상 레이어(Transaction 관련)가 하나 더 추가된다
  • payrolldomain 패키지를 transaction 없이 재사용할 수 있게 되었다.

trade-off

  • 프로젝트의 재사용성과 유지보수성은 개선되었다.
  • 하지만 패키지가 5개 추가되었고 의존 관계는 더 복잡해졌다.

결합과 캡슐화

  • 패키지내 클래스들을 캡슐화
  • default(package) 접근제어자를 이용하자.
    • public, protcted, package, private
  • ex) TimeCard, SalesReceipt

측정법

pass

측정값을 급여 관리 애플리케이션에 적용하기

  • 팩토리를 생성해서 의존성을 낮춘다.
  • 팩토리 초기화는 MainApplication에서 책임진다.
    • 아님 IoC컨테이너를 이용
  • classifications, methods, schedules, affiliations는 응집력 있는 것이 낫다.
    • 트랜잭션 별로 분류가 기능 별로 분류 보다 중요하다.
    • 어짜피 하나의 Aggregate 이기 때문이다.
    • 해당 사례에서는 이게 맞는 접근이지만, 도메인에 따라 달라질 수 있다.

요구사항 및 설계

구현

커맨드 패턴

커맨드 패턴

public inteface Command {
  void do();
}

단순한 커맨드 적용

커맨더 패턴은 명령의 개념을 캡슐화 함으로써 의존성을 없앤다.

  • 센서와 동작의 연결

Sensor가 주도하는 Command

트랜잭션

트랜잭션도 일종의 명령이므로 이를 커맨드 형식으로 캡슐화할 수 있다.

물리적, 시간적 분리

관심사 분리

되돌리기

커맨드 패턴의 undo 변형

  1. 명령스택이 존재한다
  2. 어떤 명령을 실행한다.(do)
  3. 되돌리고 싶을 시 명령스택에서 꺼낸 후 되돌린다.(undo)

명령을 취소하는 방법은 명령을 수행하는 방법을 아는 코드와 함께 있어야한다.

액티브 오브젝트 패턴

  • 커맨드 패턴의 응용
  • ActiveObjectEngineCommand객체의 연결리스트
  • 엔진에 새로운 명령을 추가할 수도 있고
  • run()을 호출할 수도 있다. - 루프를 돌면서 명령들을 실행
class ActiveObjectEngine {
  LinkedList<Command> commands = new LinkedList<>();

  public void addCommand(Command c) {
    commands.add(c);
  }

  public void run() throws Exception {
    while (!commands.isEmpty()) {
      Command c = commands.getFirst();
      commands.removeFirst();
      c.execute();
    }
  }
}
interface Command {
  void execute() throws Exception;
}

멀티스레드 상황에서 스레드 간 협력이 필요할 시 non-block 기반 처리가 가능하게 하는 패턴

재미있는 것은 말 그대로 명령이 계속 활성화된 상태로 진행된다는 점이다

템플릿 메서드 패턴

템플릿 메소드 패턴 클래스 다이어그램

  • 상속(extends)을 통한 코드 재사용을 위한 패턴
  • 상속(extends)을 통한 다형성 확보

스트레터지 패턴

스트레티지 패턴 클래스 다이어그램

  • 인터페이스 구현(implements)을 통한 다형성 확보

템플릿 메소드 패턴에 비해서 결합도가 낮음

템플릿 메서드 패턴과 스트레터지 패턴

  • 템플릿 메소드 패턴 : 일반적인 알고리즘으로 많은 구체적인 구현을 조작(상속을 통한)
  • 스트레터지 패턴 : 각각의 구체적인 구현이 다른 많은 일반적인 알고리즘에 의해 조작(위임을 통한)

퍼사드 패턴

복잡하고 일반적인 인터페이스를 가진 객체 그룹에 간단하고 구체적인 인터페이스를 제공할 때 사용

퍼사드 패턴 클래스 다이어그램

미디에이터 패턴

모든 클래스 간의 복잡한 상호작용을 캡슐화 하여 하나의 클래스에 위임하여 처리하는 패턴 각 클래스간 상호작용을 위한 관계가 필요하지 않음

미디에이터 패턴 예시 시퀀스 다이어그램

  1. 리스트 상자(aListBox)는 지시자(director:aFontDialogDirector)에게 객체에게 자신이 변경되었음을 알립니다. : widgetChanged()
  2. 지시자는 리스트 상자에서 선택된 부분이 무엇인지 알아옵니다. : getSelection()
  3. 지시자는 입력 창(aEntryField)에 선택 부분을 전달합니다. : setText()
  4. 입력 창에는 어떤 값이 포함됩니다. 지시자는 관련된 버튼을 활성화 합니다.

미디에이터 패턴 예시 클래스 다이어그램

미디에이터 패턴과 옵저버 패턴

  • 유사한 듯 하면서도 다르다.
  • 중재 객체와의 연관관계가 1:N 라는 개념의 차이가 가장 커 보인다.

퍼사트 패턴과 미디에이터 패턴

패턴 방향성 가시성 강제성
퍼사드 위로부터 단방향 가시적 강제적
미디에이터 아래로부터 양방향 비가시적 비강제적

싱글톤 패턴

모노스테이트 패턴

public class Monostate {
    private static int x = 0;

    public Monostate() {

    }

    public void setX(int x) {
        this.x = x;
    }

    public int getX() {
        return x;
    }
}

싱글톤 패턴과 모노스테이트 패턴

널 오브젝트 패턴

널 오브젝트 패턴 예시 클래스 다이어그램

널 오브젝트 적용 전과 후

before

Employee e = employeeDao.getEmployee("Bob");
if (e != null && e.isTimeToPay(today))
  e.pay();

after

Employee e = employeeDao.getEmployee("Bob");
if (e.isTimeToPay(today))
  e.pay();

null 비교가 없어져서 코드 가독성이 좋아지고 간결해짐

널 오브젝트 상수화

  • 그래도 null 비교와 같은 처리도 필요할 수 있다. 그래서 상수화 시키면 아래와 같은 처리가 가능하다.
if (e == Employe.NULL) {
  ...
}