#1 교육정리
1) GRASPH 패턴
2) GoF의 Design Pattern: Factory Method 패턴
#2 GRASPH 패턴
GRASP 패턴은 General Responsibility Assignment Software Patterns의 축약어이다. Object-Oriented 디자인의 핵심 문제는 각 객체에 역할(또는 책임)을 부여하는 것이다. GRASP Pattern은 역할 부여의 원칙들을 말하고 있다. 일반적으로 디자인 패턴이라고 불리우는 것들처럼 구체적인 구조는 없지만, 각 디자인 패턴들은 GRASP 패턴이 제시하는 철학를 각 상황에서 구체적으로 구현할 것이라 볼 수 있다.
9가지 구성)
- Information Expert(전문가 패턴): 역할을 수행할 수 있는 정보를 가지고 있는 객체에 역할을 부여하자. 단순해 보이는 이 원칙은 객체지향의 기본 원리 중에 하나이다. 객체는 데이터와 처리로직이 함께 묶여 있는 것이고, 자신의 데이터를 감추고자(정보은닉) 하면 오직 자기 자신의 처리 로직(메서드)에서만 데이터를 처리하고, 외부에는 그 기능(역할)만을 제공해야 하기 때문이다.
- Creator(생성자 패턴): 객체의 생성은 생성되는 객체의 컨텍스트를 알고 있는 다른 객체가 있다면, 컨텍스트를 알고 있는 객체에 부여하자. A 객체와 B 객체의 관계의 관계가 다음 중 하나라면 A의 생성을 B의 역할로 부여하라.
- B 객체가 A 객체를 포함하고 있다.
- B 객체가 A 객체의 정보를 기록하고 있다.
- A 객체가 B 객체의 일부이다.
- B 객체가 A 객체를 긴밀하게 사용하고 있다.
- B 객체가 A 객체의 생성에 필요한 정보를 가지고 있다. - Controller(제어기 패턴): 시스템 이벤트(사용자의 요청)를 처리할 객체를 만들자. 시스템, 서브시스템으로 들어오는 외부 요청을 처리하는 객체를 만들어 사용하라. 만약 어떤 서브시스템안에 있는 각 객체의 기능을 사용할 때, 직접적으로 각 객체에 접근하게 된다면 서브시스템과 외부간의 Coupling(서로 상호작용하는 시스템들간의 의존성)이 증가되고, 서브시스템의 어떤 객체를 수정할 경우, 외부에 주는 충격이 크게 된다. 서브시스템을 사용하는 입장에서 보면, 이 Controller 객체만 알고 있으면 되므로 사용하기 쉽다.
- Low Coupling(저결합성 패턴): 객체들간, 서브 시스템들간의 상호의존도가 낮게 역할을 부여하자. Object-Oriented 시스템은 각 객체들과 그들 간의 상호작용을 통하여 요구사항을 충족시키는 것을 기본으로 한다. 그러므로, 각 객체들 사이에 Coupling이 존재하지 않을 수는 없다. 이 패턴은 요구사항은 충족시키면서도 각 객체들, 각 서브시스템 간의 Coupling를 낮은 수준으로 유지하는 방향으로 디자인하라고 말하고 있다. Low Coupling은 각 객체, 서브시스템의 재 사용성을 높이고, 시스템 관리에 편하게 한다.
- High Cohesion(고응집성 패턴): 각 객체가 밀접하게 연관된 역할들만 가지도록 역할을 부여하자. 이 패턴은 Low Coupling 패턴과 동전의 양면을 이루는 것으로, 한 객체, 한 서브시스템이 자기 자신이 부여받은 역할만을 수행하도록 짜임새 있게 구성되어 있다면, 자신이 부여 받은 역할을 충족시키기 위해 다른 객체나 시스템을 참조하는 일이 적을 것이고, 그것이 곧 Low Coupling이기 때문이다.
- Polymorphism(다형성 패턴): 객체의 종류에 따라 행동양식이 바뀐다면, Polymorphism 기능을 사용하자. Object-Oriented 시스템은 상속과 Polymorphism(다형성)을 지원한다. 만약 객체의 종류에 따라 행동이 바뀐다면 객체의 종류를 체크하는 조건문을 사용하지 말고, Object-Oriented 시스템의 Polymorphism 기능을 사용하라.
- Pure Fabrication(순수 가공 패턴): Information Expert 패턴을 적용하면 Low Coupling과 High Cohesion의 원칙이 깨어진다면, 기능적인 역할을 별도로 한 곳으로 모으자. 데이터베이스 정보를 저장하거나, 로그 정보를 기록하는 역할에 대해 생각해 보자. 각 정보는 각각의 객체들이 가지고 있을 것이다. 이 때 Information Expert 패턴을 적용하면, 각 객체들이 정보를 저장하고, 로그를 기록하는 역할을 담당해야 하지만, 실제로 그렇게 사용하는 사람들은 없다. 이것은 그 기능들이 시스템 전반적으로 사용되고 있기 때문에 각 객체에 그 기능을 부여하는 것은 각 객체들이 특정 데이터베이스에 종속을 가져오거나, 로그을 기록하는 매커니즘을 수정할 경우, 모든 객체를 수정해야 하는 결과를 가져온다. 즉 Low Coupling의 원칙이 깨어지게 된다. 이럴 경우에는 공통적인 기능을 제공하는 역할을 한 곳으로 모아서 가상의 객체, 서브시스템을 만들어라.
- Indirection(간접 패턴): 두 객체 사이의 직접적인 Coupling을 피하고 싶으면, 그 사이에 다른 객체를 사용하라. 여기서 말하는 다른 객체란 인터페이스가 될 수 있고, 주로 인터페이스인 경우가 많다. 그런 특별한 경우는 아래에 설명된 Protected Variations 패턴이라고 부를 수 있다.
- Protected Variations(보호 변형 패턴): 변경될 여지가 있는 곳에 안정된 인터페이스를 정의해서 사용하자. JDBC(Java Database Connectivity - 자바에서 데이터베이스에 접속 가능도록 하는 자바API))에 대해서 생각해 보자. JDBC는 일련의 인터페이스들로 구성되어 있으며, 각 데이터베이스 벤더들이 인터페이스를 구현한 Concrete 클래스를 제공하고 있다. 데이터베이스 기능을 사용하는 시스템의 입장에선 각 벤더들이 구현방식을 바꾸었을 때, 자신의 코드를 수정하고 싶지 않을 것이다. 그래서 Driver를 로딩하는 코드를 제외하고는 모두 인터페이스를 사용함으로서 데이터베이스의 변경시에도 Driver 로딩만 바꾸어 주면 되도록 데이터베이스 관련 작업이 필요한 곳에는 안정된 JDBC 인터페이스를 사용한 것이다.
출처 : https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS8586826397
참고자료 : http://contents.kocw.or.kr/KOCW/document/2014/Seowon/SongHaesang/08.pdf
#3 GoF의 Design Pattern: Factory Method 패턴
1995년 GoF(Gang of Four)라고 불리는 Erich Gamma, Richard Helm, Ralph Johnson, John Vissides가 처음으로 디자인 패턴을 구체화하였다. GoF의 디자인 패턴은 소프트웨어 공학에서 가장 많이 사용되는 디자인 패턴이다. 목적에 따라 분류할 시 생성 패턴 5개, 구조 패턴 7개, 행위 패턴 11개, 총 23개의 패턴으로 구성된다.
GoF의 Design Pattern 분류)
GoF 디자인 패턴을 분류하는 기준은 두 가지입니다.
첫째로, 목적에 따라 분류하면 생성, 구조, 행동 3가지로 나눌 수 있다. 각각의 패턴이 어떤 일을 하기 위한 것인지에 관한 것이다. 생성 패턴은 객체의 생성 과정에 관여, 구조 패턴은 객체의 합성에 관여, 행동 패턴은 객체가 상호작용하는 방법이나 관심사를 분리하는 방법에 관여한다.
둘째로 범위에 따라 분류할 수도 있다. 패턴을 주로 클래스에 적용하는지, 객체에 적용하는 지 구분하는 것이다. 클래스 패턴은 클래스와 서브클래스 간의 관련성을 다룬다. 주로 상속을 통해 관련되며, 컴파일 타임에 정적으로 결정된다. 객체 패턴은 객체 간의 관련성을 다루고, 런타임에 변경될 수 있는 동적인 성격을 가진다.
표와 같이 보면 아래와 같습니다.
출처 : https://4z7l.github.io/2020/12/25/design_pattern_GoF.html
생성 패턴)
생성 패턴은 객체의 생성과 관련된 패턴이다. 객체의 인스턴스 과정을 추상화하는 방법이다. 객체의 생성과 참조 과정을 캡슐화하여 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 받지 않도록 하여 프로그램에 유연성을 더해준다. 생성 클래스 패턴은 객체를 생성하는 일부를 서브클래스가 담당하도록 하며, 생성 객체 패턴은 객체 생성을 다른 객체에게 위임한다.
객체 | 의도 |
추상 팩토리(Abstract Factory) | 구체적인 클래스를 지정하지 않고 인터페이스를 통해 서로 연관되는 객체들을 그룹으로 표현함 |
빌더(Builder) | 복합 객체의 생성과 표현을 분리하여 동일한 생성 절차에서도 다른 표현 결과를 만들어낼 수 있음 |
팩토리 메소드(Factory Method) | 객체 생성을 서브클래스로 위임하여 캡슐화함 |
프로토타입(Prototype) | 원본 객체를 복사함으로써 객체를 생성함 |
싱글톤(Singleton) | 어떤 클래스의 인스턴스는 하나임을 보장하고 어디서든 참조할 수 있도록 함 |
구조 패턴)
구조 패턴은 클래스나 객체들을 조합해 더 큰 구조로 만들 수 있게 해주는 패턴이다. 구조 클래스 패턴은 상속을 통해 클래스나 인터페이스를 합성하고, 구조 객체 패턴은 객체를 합성하는 방법을 정의한다.
객체 | 의도 |
어댑터(Adapter) | 클래스의 인터페이스를 다른 인터페이스로 변환하여 다른 클래스가 이용할 수 있도록 함 |
브리지(Bridge) | 구현부에서 추상층을 분리하여 각자 독립적으로 확장할 수 있게 함 |
컴포지트(Composite) | 객체들의 관계를 트리 구조로 구성하여 복합 객체와 단일 객체를 구분없이 다룸 |
데코레이터(Decorator) | 주어진 상황 및 용도에 따라 어떤 객체에 다른 객체를 덧붙이는 방식 |
퍼싸드(Facade) | 서브시스템에 있는 인터페이스 집합에 대해 하나의 통합된 인터페이스(Wrapper) 제공 |
플라이웨이트(Flyweight) | 크기가 작은 여러 개의 객체를 매번 생성하지 않고 가능한 한 공유할 수 있도록 하여 메모리를 절약함 |
프록시(Proxy) | 접근이 어려운 객체로의 접근을 제어하기 위해 객체의 Surrogate나 Placeholder를 제공 |
행위 패턴)
행위 패턴은 클래스나 객체들이 서로 상호작용하는 방법이나 어떤 태스크, 어떤 알고리즘을 어떤 객체에 할당하는 것이 좋을지를 정의하는 패턴이다. 즉, 객체나 클래스의 교류 방법에 대해 정의하는 것이다.
행위 패턴은 하나의 객체로 수행할 수 없는 작업을 여러 객체로 분배하면서 그들 간의 결합도를 최소화 할 수 있도록 도와준다. 행위 클래스 패턴은 상속을 통해 알고리즘과 제어 흐름을 기술하고, 행위 객체 패턴은 하나의 작업을 수행하기 위해 객체 집합이 어떻게 협력하는지를 기술한다.
객체 | 의도 |
책임 연쇄(Chain of Responsibility) | 요청을 받는 객체를 연쇄적으로 묶어 요청을 처리하는 객체를 만날 때까지 객체 Chain을 따라 요청을 전달함 |
커맨드(Command) | 요청을 객체의 형태로 캡슐화하여 재사용하거나 취소할 수 있도록 저장함 |
인터프리터(Interpreter) | 특정 언어의 문법 표현을 정의함 |
반복자(Iterator) | 내부를 노출하지 않고 접근이 잦은 어떤 객체의 원소를 순차적으로 접근할 수 있는 동일한 인터페이스 제공 |
중재자(Mediator) | 한 집합에 속해있는 객체들의 상호작용을 캡슐화하여 새로운 객체로 정의 |
메멘토(Memento) | 객체가 특정 상태로 다시 되돌아올 수 있도록 내부 상태를 실체화 |
옵서버(Observer) | 객체 상태가 변할 때 관련 객체들이 그 변화를 통지받고 자동으로 갱신될 수 있게 함 |
상태(State) | 객체의 상태에 따라 동일한 동작을 다르게 처리해야할 때 사용 |
전략(Strategy) | 동일 계열의 알고리즘군을 정의하고 캡슐화하여 상호교환이 가능하도록 함 |
템플릿 메소드(Template Method) | 상위클래스는 알고리즘의 골격만을 작성하고 구체적인 처리는 서브클래스로 위임함 |
방문자(Visitor) | 객체의 원소에 대해 수행할 연산을 분리하여 별도의 클래스로 구성함 |
Factory Method)
팩토리 메소드 패턴은 객체 생성을 공장(Factory) 클래스로 캡슐화 처리하여 대신 생성하게 하는 생성 디자인 패턴이다. 즉, 클라이언트에서 직접 new 연산자를 통해 제품 객체를 생성하는 것이 아닌, 제품 객체들을 도맡아 생성하는 공장 클래스를 만들고, 이를 상속하는 서브 공장 클래스의 메서드에서 여러가지 제품 객체 생성을 각각 책임 지는 것이다.
또한 객체 생성에 필요한 과정을 템플릿 처럼 미리 구성해놓고, 객체 생성에 관한 전처리나 후처리를 통해 생성 과정을 다양하게 처리하여 객체를 유연하게 정할 수 있는 특징도 있다.
팩토리 메서드 패턴 구조)
- Creator : 최상위 공장 클래스로서, 팩토리 메서드를 추상화하여 서브 클래스로 하여금 구현하도로 함
- 객체 생성 처리 메서드(someOperartion) : 객체 생성에 관한 전처리, 후처리를 템플릿화한 메소드
- 팩토리 메서드(createProduct) : 서브 공장 클래스에서 재정의할 객체 생성 추상 메서드
- ConcreteCreator : 각 서브 공장 클래스들은 이에 맞는 제품 객체를 반환하도록 생성 추상 메소드를 재정의한다. 즉, 제품 객체 하나당 그에 걸맞는 생산 공장 객체가 위치된다.
- Product : 제품 구현체를 추상화
- ConcreteProduct : 제품 구현체
정리하자면, 팩토리 메소드 패턴은 객체를 만들어내는 공장(Factory 객체)을 만드는 패턴이라고 보면 된다. 그리고 어떤 클래스의 인스턴스를 만들지는 미리 정의한 공장 서브 클래스에서 결정한다.
객체 생성을 이렇게 번거롭게 구성하는 이유는 객체간의 결합도가 낮아지고 유지보수에 용이해지기 때문이다.
팩토리 메서드 패턴 흐름)
클래스 구성은 아래와 같습니다.
- 제품(Product) 클래스
// 제품 객체 추상화 (인터페이스)
interface IProduct {
void setting();
}
// 제품 구현체
class ConcreteProductA implements IProduct {
public void setting() {
}
}
class ConcreteProductB implements IProduct {
public void setting() {
}
}
- 공장(Factory) 클래스
// 공장 객체 추상화 (추상 클래스)
abstract class AbstractFactory {
// 객체 생성 전처리 후처리 메소드 (final로 오버라이딩 방지, 템플릿화)
final IProduct createOperation() {
IProduct product = createProduct(); // 서브 클래스에서 구체화한 팩토리 메서드 실행
product.setting(); // .. 이밖의 객체 생성에 가미할 로직 실행
return product; // 제품 객체를 생성하고 추가 설정하고 완성된 제품을 반환
}
// 팩토리 메소드 : 구체적인 객체 생성 종류는 각 서브 클래스에 위임
// protected 이기 때문에 외부에 노출이 안됨
abstract protected IProduct createProduct();
}
// 공장 객체 A (ProductA를 생성하여 반환)
class ConcreteFactoryA extends AbstractFactory {
@Override
public IProduct createProduct() {
return new ConcreteProductA();
}
}
// 공장 객체 B (ProductB를 생성하여 반환)
class ConcreteFactoryB extends AbstractFactory {
@Override
public IProduct createProduct() {
return new ConcreteProductB();
}
}
- 클래스 흐름
class Client {
public static void main(String[] args) {
// 1. 공장 객체 생성 (리스트)
AbstractFactory[] factory = {
new ConcreteFactoryA(),
new ConcreteFactoryB()
};
// 2. 제품A 생성 (안에서 createProduct() 와 생성 후처리 실행)
IProduct productA = factory[0].createOperation();
// 3. 제품B 생성 (안에서 createProduct() 와 생성 후처리 실행)
IProduct productB = factory[1].createOperation();
}
}
팩토리 메서드 패턴 특징)
- 패턴 사용 시기
- 클래스 생성과 사용의 처리 로직을 분리하여 결합도를 낮추고자 할 때
- 코드가 동작해야 하는 객체의 유형과 종속성을 캡슐화를 통해 정보 은닉 처리 할 경우
- 라이브러리 혹은 프레임워크 사용자에게 구성 요소를 확장하는 방법을 제공하려는 경우
- 기존 객체를 재구성하는 대신 기존 객체를 재사용하여 리소스를 절약하고자 하는 경우
- 상황에 따라 적절한 객체를 생성하는 코드는 자주 중복될 수 있다. 그리고 객체 생성 방식의 변화는 해당되는 모든 코드 부분을 변경해야 하는 문제가 발생한다.
- 따라서 객체의 생성 코드를 별도의 클래스 / 메서드로 분리 함으로써 객체 생성의 변화에 대해 대비를 하기 위해 팩토리 메서드 패턴을 이용한다고 보면 된다.
- 특정 기능의 구현은 별개의 클래스로 제공되는 것이 바람직한 설계이기 때문이다.
- 패턴 장점
- 생성자(Creator)와 구현 객체(concrete product)의 강한 결합을 피할 수 있다.
- 팩토리 메서드를 통해 객체의 생성 후 공통으로 할 일을 수행하도록 지정해줄 수 있다.
- 캡슐화, 추상화를 통해 생성되는 객체의 구체적인 타입을 감출 수 있다.
- 단일 책임 원칙 준수 : 객체 생성 코드를 한 곳 (패키지, 클래스 등)으로 이동하여 코드를 유지보수하기 쉽게 할수 있으므로 원칙을 만족
- 개방/폐쇄 원칙 준수 : 기존 코드를 수정하지 않고 새로운 유형의 제품 인스턴스를 프로그램에 도입할 수 있어 원칙을 만족 (확장성 있는 전체 프로젝트 구성이 가능)
- 생성에 대한 인터페이스 부분과 생성에 대한 구현 부분을 따로 나뉘었기 때문에 패키지 분리하여 개별로 여러 개발자가 협업을 통해 개발
- 패턴 단점
- 각 제품 구현체마다 팩토리 객체들을 모두 구현해주어야 하기 때문에, 구현체가 늘어날때 마다 팩토리 클래스가 증가하여 서브 클래스 수가 폭발한다.
- 코드의 복잡성이 증가한다.
출처 : https://inpa.tistory.com/entry/GOF-💠-팩토리-메서드Factory-Method-패턴-제대로-배워보자#
'[Naver Cloud Camp 7] 교육 정리' 카테고리의 다른 글
네이버 클라우드 캠프 34일차 230613 (0) | 2023.06.13 |
---|---|
네이버 클라우드 캠프 33일차 230612 (0) | 2023.06.13 |
네이버 클라우드 캠프 31일차 230608 (0) | 2023.06.08 |
네이버 클라우드 캠프 30일차 230607 (0) | 2023.06.07 |
네이버 클라우드 캠프 29일차 230605 (0) | 2023.06.05 |