글

라벨이 OOP인 게시물 표시

디자인 패턴 #5 - 컴포지트 패턴 (composite pattern)

이미지
1. 컴포지트 패턴 (composite pattern) 객체들의 관계를 트리 구조로 구성하여 부분-전체를 나타내는 계층 구조로 만들 수 있다. 이 패턴을 이용하면 클라이언트에서 단일 객체와 여러 객체들로 구성된 복합 객체 모두 동일하게 다루도록 한다. 쉽게 표현하자면 담는 그릇과 내용물을 동일하게 다루는 것으로 디렉토리 트리 구조가 이에 해당한다. 파일 관리자와 같은 디렉토리 트리 구조는 디렉토리와 파일을 동일한 개념으로 다룬다. 사용자가 디렉토리의 depth를 변경하며 이동할 수 있고, 특정 디렉토리에 속한 디렉토리와 파일 목록을 보여준다. 디렉토리와 파일을 담고 있는 디렉토리도 같은 레벨의 파일과 동일하게 취급하게 된다.(이해가 어려우면 당장 핸드폰의 파일관리자를 실행해보라.) 파일관리자와 클래스 다이어그램을 비교하자면 Composite 클래스는 디렉토리를, Leaf를 파일을 의미한다. Composite 클래스와 Leaf 클래스는 Component 추상 클래스를 상속받았기 때문에 동일하게 취급할 수 있다. 이러한 composite 패턴의 특징을 이용해 디렉토리 트리 구조를 만들고, 다룰 수 있다. Component는 추상 클래스로 Directory의 reference를 가지고 있다. add(), getChildren()은 필요 시 하위 클래스에서 override하여 구현한다. mParentDirectory는 부모 디렉토리 참조이며, getAbsolutePath() 호출 시 재귀호출을 통해 절대 경로를 완성한다. 클래스 다이어그램에 나타난 것과 같이 Directory 클래스는 하위 클래스를 가질 수 있고, 이를 지원하기 위한 메소드를 override하여 정의한다. File 클래스는 Leaf에 해당하는 클래스로 트리 구조의 마지막에 위치한다.

디자인 패턴 #4 - 싱글턴 패턴 (singleton pattern)

이미지
1. 싱글턴 패턴 (singleton pattern) 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 return한다. 즉 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있다. 보통 getInstance()로 불리오는 정적 메소드를 호출하는 형식으로 구현된다. 클래스 다이어그램을 살펴보면 클래스 내부의 singleton 객체와 생성자는 private로 외부에서 접근이 불가하나, getInstance()를 통해 객체를 생성하고 얻을 수 있도록 접근제어자가 public으로 설정되어 있다. 코드를 살펴보면 위에 언급한대로 구현되어 있는 상태이다. getInstance() 메소드를 살펴보면 동기화(syncronized)처리가 되어 있는 것을 확인할 수 있는데, 다중 thread 상황에서 mSingleton 객체가 만들어지기 전에 2개 이상의 thread가 접근해 객체를 여러 개 만드는 경우가 발생할 수 있다. 이를 위해 syncronized 처리를 한 것인데, 동기화 처리를 하면 속도가 많이 저하된다. 객체가 2개 이상 만들어 질 수 있는 때는 최초 생성하는 경우인데, 동기화에 따르는 비용이 크기 때문에 큰 문제가 아니라면 그냥 두는 것도 방법일 수 있다.

디자인 패턴 #3 - 데코레이터 패턴 (decorator pattern)

이미지
1. 데코레이터 패턴 (decorator pattern) 데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다. 쉽게 말해 샌드위치와 같이 본래의 재료에 무언가를 추가할 때 사용된다. 클래스 다이어그램을 살펴보면 Component는 기본적인 재료를 의미한다. 샌드위치로 예를 들자면 Component는 "빵"이라고 할 수 있다. 기본 재료인 "빵"에 Decorator 를 상속받은 ConcreteDecoratorA, ConcreteDecoratorB를 사용하여 각종 햄, 야채, 소스를 추가하면 된다. Decorator 객체는 Component의 reference를 가지고 있고, 이 곳에 꾸며야 할 객체(ConcreteComponent)를 전달받아 여러 가지 동작/정보를 추가하게 된다. Beverage 클래스는 Component에 해당하는 객체로 기본적인 음료를 나타내는 추상 클래스이다. CondimentDecorator 클래스는 Decorator의 역할을 하는 클래스로 첨가물을 나타내는 추상클래스이다. 전달받은 Beverage 클래스를 저장하여, 꾸며 주게(첨가물 추가) 된다. DarkRoast 객체는 Beverage를 상속받은 클래스로 ConcreteComponent의 역할을 한다. 명칭과 가격을 정의했다.  마지막으로 Moca 클래스는 ConcreteDecorateA의 역할을 하며, 넘겨받는 DarkRoast 클래스에 Moca를 추가하고, 명칭과 가격을 꾸며주게 된다. 사용하는 코드는 다음과 같다. 자바 I/O 패키지에 데코레이터 패턴이 다수 적용되어 있다. FileInputStream → BufferedInputStream → LineNumberInputStream으로 이어지는 확장 구조를 가지고 있다. FileInputSream은 파일을 읽어들이는 기본 동작을 정의하며...

디자인 패턴 #2 - 옵저버 패턴 (observer pattern)

이미지
1. 옵저버 패턴 (observer pattern) 옵저버 패턴(observer pattern)은 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 가지는 구조를 의미한다. 쉽게 말해 여행 준비를 할 때 '준비가 완료되었는지 매 순간 확인하지 않고, 준비가 완료되면 연락받는 것'과 같다. 옵저버 패턴은 느슨한 결합(loose coupling)으로 두 객체가 연결되는데, 이는 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다. 옵저버(연락을 받는 객체)가 연락을 받고 싶다면 주체(연락을 하는 객체)에서 정의한 interface를 구현한 뒤 주체에게 등록만 하면 된다. 주체는 옵저버가 무엇을하는지 어떤 클래스를 상속받고 있는지 등에 대해 전혀 알지 않아도 되며, 주체 또는 옵저버가 바뀌더라도 서로에게 영향을 미치지 않는다. 각각의 코드를 살펴보면 ConcretePublischer 객체는 연락을하는 주체로 Observer가 등록되면 ArrayList로 Observer관리하고 이벤트 발생 시 loop를 돌며 등록된 observer에 연락을 준다. registerObserver()를 통해 옵저버를 등록할 수 있으며, unregisterObserver()를 통해 등록된 옵저버를 제거할 수 있다. 연락을 받는 옵저버는 옵저버 interface를 구현한 뒤 Publisher에게 연락을 받기 위해 등록하면 연락받을 준비가 완료되며, 이 후 Publisher에서 Callback이 오면 해당 동작을 처리하면된다. 옵저버 패턴을 사용하면 어떤 정보가 필요한 경우 매 번 확인을 하지 않고, 이벤트가 발생하는 순간에 주체로부터 연락을 받을 수 있다. 옵저버 패턴의 일반적인 사례는 사용자 입력에 대한 이벤트(OnClickListener) 처리가 있으며, MVC(Model-View-Controller) 패턴과 자주 ...

디자인 패턴 (design pattern)

이미지
1. 디자인 패턴 (design pattern) 디자인 패턴은 개발 과정에서 겪게되는 공통된 문제들에 대해 전문가들의 경험을 모아 표준적인 해법을 구조화한 것으로, 디자인 패턴을 숙지하면 개발자간의 원활한 커뮤니케이션이 가능해진다. 디자인 패턴은 GoF(Gang of Four)에 의한 고안된 것으로 보는 것이 일반적이다. GoF는 에릭 감마(Erich Gamma), 리처드 헬름(Richard Helm), 랄프 존슨(Ralph Johnson), 존 블리스데스(John Vlissides)에 의해 고안된 것으로 알려져있다. 2. SOLID 원칙 좋은 객체지향 설계를 위해서는 기본적으로 5가지 원칙을 따르는 것이 좋다. 5가지 원칙은 각각의 앞글자를 따서 SOLID라고 부른다. 좋은 설계란 응집도는 높게, 결합도는 낮게 설계된 것을 이야기한다. - SRP (Single Responsibility Principle, 단일 책임 원칙) 객체는 단 하나의 책임만 가져야한다. 즉 어떤 변화에 의해 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 함을 의미한다. - OCP (Open-Closed Principle, 개방-폐쇄 원칙) 변경에는 닫혀있고, 확장에는 열려있어야한다. 요구사항의 변경이나 추가가 발생하더라도, 기존 코드에는 수정이 일어나지 않고, 쉽게 확장할 수 있어야 한다는 의미이다. - LSP (Liskov Substitution Principle, 리스코프 치환 원칙) 자식 클래스는 부모 클래스의 역할을 대체할 수 있어야한다. 자식 클래스는 부모 클래스의 책임을 무시하거나 재정의하지 않고, 확장만 수행해야한다. - ISP (Interface Segregation Principle, 인터페이스 분리 원칙) 사용하지 않는 인터페이스는 구현하지 말아야한다. 하나의 일반적인 interface보다는 여러 개의 구체적인 interface가 낫다. - DIP (Dependency Inversion Principle, 의존 역전 원칙) ...

이 블로그의 인기 게시물

아스키 코드(ASCII)와 유니코드(unicode)

Kubernetes 학습 노트

Innovation or Threat? Why Tech Giants Are Issuing a Total Ban on OpenClaw (혁신인가, 위협인가? 테크 거물들이 오픈클로를 전면 금지하는 이유)