반응형
더보기
목차
- Factory Method Pattern
- Enum Factory Method Pattern
예상 문제
- XML document Parser 코드 주고, 어떤 디자인 패턴이 필요한지
- 이 패턴의 장/단점
Factory Method Pattern
- 팩토리 메소드 패턴이란?
- 객체 생성을 서브 클래스에 위임할 수 있는 생성 패턴
- 객체를 생성하기 위한 인터페이스를 정의하지만, 어떤 클래스를 인스턴스화할지는 서브 클래스가 결정함
- 팩토리 메소드 패턴이 사용되는 상황
- 객체의 생성을 코드의 나머지 부분으로 분리 하려고 할 때
- 사용자가 자체 객체를 만들어 시스템을 확장하는 방법을 제공하고자 할 때
- 객체 생성에 있어서 기존의 코드를 건들지 않고, 쉽게 확장할 수 있는 방법이 있을까? (OCP 원칙)
- Example situation
- 배를 만드는 공장 (ShipFactory) 에서 Client 의 요청에 맞추어 배를 주문해서 (orderShip) 배 (Ship) 의 객체를 받는 상황으로 비유
public class Client{ public static void main(String[] args){ Client client = new Client(); Ship whiteship = ShipFactory.orderShip("whiteship"); System.out.println(whiteship); Ship blackship = ShipFactory.orderShip("blackship"); System.out.println(whiteship); } } public class ShipFactory{ public static Ship orderShip(String name){ validate(name); prepareFor(name); Ship ship = new Ship(); ship.setName(name); if(name.equalsIgnoreCase("whiteship")) ship.setColor("white"); else if(name.equlsIgnoreCase("blackship")) ship.setColor("black"); return ship; } private static void validate(String name){ if(name==null || name.isBlank()) //throw an exception } } public class ship{ private String name; private String color; // getters and setters }
- 요구사항 변경으로 새로운 배(blueship) 이 추가되어야 한다면?
- Ship 클래스에 새로운 멤버 변수가 추가된다면?
=> ShipFactory 와 Ship 사이의 강한 결합
=> 확장을 시도하게 되면 기존의 코드에 수정이 발생할 수 밖에 없는 구조 (OCP 위배)
- 배를 만드는 공장 (ShipFactory) 에서 Client 의 요청에 맞추어 배를 주문해서 (orderShip) 배 (Ship) 의 객체를 받는 상황으로 비유
- 팩토리 메소드 패턴의 구조
- 인터페이스를 통해 팩토리 객체와 제품 객체 간 결합을 느슨하게 함
- 팩토리 클래스는 제품 객체를 도맡아 생성하고 이를 상속하는 서브 클래스의 메소드에서 여러 가지 제품 객체 생성을 각각 책임 지는 것
- 인터페이스를 통해 팩토리 객체와 제품 객체 간 결합을 느슨하게 함
- 팩토리 메소드 패턴으로 객체 생성시 시퀀스
- ConcreteProductA 에 대한 구체적인 생성은 ConcreteCreatorA (팩토리) 객체가 전담
- Client 는 ConcreateCreatorA 를 통해서 Product 객체를 받는다.
- ConcreteProductA product = new ConcreateCreatorA().createProduct()
- Example situation
- ShipFactory 에 팩토리 메소드 패턴을 적용해서 코드를 수정
public interface Ship{ void setName(String name); void setColor(String color); } public class ConcreteShip implements Ship{ private String name; private String color; @Override void setName(String name){ this.name = name; } @Override void setColor(String color){ this.color = color; } } public class WhiteShip extends ConcreteShip{ public WhiteShip(){ setName("whiteship"); setColor("white"); } } //-------------------------------------------- public interface ShipFactory{ default Ship orderShip(String name){ validate(name); prepareFor(name); Ship ship = createShip(); return ship; } Ship createShip(); // to be overrided private void validate(String name){ if(name == null || name.isblank()) //throw an exception } public static void prepareFor(String name){ //print } } public class WhiteShipFactory implements ShipFactory{ @Override public Ship createShip(){ return new WhiteShip(); } } //--------------------------------------------- public class Client{ public static void main(String[] args){ Ship whiteship = new WhiteShipFactory().orderShip("whiteship"); System.out.println(whiteship); } }
- 새로운 BlackShip을 추가하려고 할 때, 팩토리 메소드 패턴이 적용된 구조에서는 기존 코드의 수정이 없음!
public class BlackShip extends ConcreteShip{ public BlackShip(){ setName("blackship"); setColor("black"); } } //--------------------------------------------- public class Client{ public static void main(String[] args){ Ship blackship = new BlackShipFactory().orderShip("blackship"); System.out.println(blackship); } }
- Client 코드도 추가 수정이 최소화되도록 수정해 볼 수 있음
- 인터페이스로 코드가 구성되었기 때문에 가능 (코드의 중복을 줄임)
public class Client{ private void print(ShipFactory shipFactory, String name){ System.out.println(shipFactory.orderShip(name)); } public static void main(String[] args){ Client client = new Client(); client.print(new WhiteShipFactory(), "whiteship"); client.print(new BlackShipFactory(), "blackship"); } }
- 인터페이스로 코드가 구성되었기 때문에 가능 (코드의 중복을 줄임)
- ShipFactory 에 팩토리 메소드 패턴을 적용해서 코드를 수정
- XML document parser
- 여러 가지 파싱 방법을 제공하기 위해 팩토리 메소드 패턴으로 구현됨
- 다른 사용 사례
- Java.util.Calendar, java.text.Numberformant, Spring BeanFactory, etc.
- 장점
- 객체의 생성을 캡슐화 하여 나머지 코드와 분리 시키기 때문에 코드의 유지보수를 용이하게 함
- 객체의 생성이 인터페이스를 통해 이루어지므로 인터페이스 기반의 재사용이 용이함 (Client 코드의 변경 예시)
- SRP 준수
- 팩토리 메소드 패턴은 객체의 생성 및 초기화에 관한 책임만 가지고 있음
- OCP 준수
- 기존 코드의 변경 없이 새로운 코드를 유연하게 추가할 수 있음
- 객체를 생성하기 위한 인터페이스를 정의하지만 어떤 클래스를 인스턴스화 할지는 서브클래스가 결정함
- 단점
- 코드 구조 복잡도 증가
- 각 제품 구현체 마다 팩토리 객체를 모두 구현 해주어야 하기 때문에 클래스 수가 많아지며 한 눈에 이해하기 어려움
- 추가적인 클래스 객체가 생성되기 때문에 객체 생성에 따른 오버헤드가 발생할 수 있고 성능 영향을 미칠 수 있음
- 코드 구조 복잡도 증가
- 기타 사항
- 팩토리 메소드 패턴의 여러 개선된 변형이 존재
- Enum factory pattern
- 서브 팩토리 클래스를 하나하나 구현하지 않고 enum Factory 하나로 구성하려고 하는 것
- Dynamic factory pattern
- 서브 클래스 수가 늘어 나는 것을, Reflection API 를 이용해 동적으로 처리하여 문제를 해결
- Enum factory pattern
- 팩토리 메소드 패턴의 여러 개선된 변형이 존재
Enum Factory Method Pattern
- Factory Method Pattern 의 문제점
- 제품의 객체의 수 마다 팩토리 서브 클래스를 모두 구현해주어야 함
- 팩토리 클래스는 한 번만 인스턴스화 하고 재사용하면서 제품 객체를 생성하는 역할만 하면 되는데, 이전의 코드는 여러 팩토리 객체가 생기면서 낭비될 여지가 없음
- Enum 으로 팩토리 메소드 패턴을 구성해 준다면, 일일이 서브 팩토리 클래스 구현 없이 하나의 enum Factory 에서 구성할 수 있음
- 예시 - 도형에 대한 팩토리 메소드 패턴
class Client{ public static void main(String[] args){ Shape rectangle = new RectangleFactory().create("red"); rectangle.draw(); Shape circle = new CircleFactory().create("yellow"); circle.draw(); } }
- Factory 객체가 여러번 생성됨
(꼭 여러번 생성 되어야 할까? 한 번만 생성되어도 되지 않을까?)
- Factory 객체가 여러번 생성됨
- Enum 을 활용한 해결 아이디어
- 추가적인 팩토리 클래스를 만들지 않아야 하면서 동시에 단일 팩토리로 제품 객체를 생성할 수 있어야 함
- Enum 을 이용하면 싱글톤 객체를 만들 수 있음
enum Season{ SUMMER("여름"); private String season; //문자열을 저장할 필드 private Season(String season){ //생성자 (싱글톤) this.season = season; } public String getSeason(){ //Getter return season; } } public static void main(String[] args){ Season s = Season.SUMMER; System.out.println(s.name()); // 열거 객체명 출력 : SUMMER System.out.println(s.getSeason()); //매핑된 열거 데이터 출력 : 여름 }
- Enum 을 이용하면 멀티톤 (multiton) 으로도 일반화 (2가지 방식)
- Multiton 이란 여러 개의 관리되는 인스턴스를 가지는 클래스를 말함(1)
enum Season{ Spring("봄"); SUMMER("여름"); FALL("가을"); WINTER("겨울"); private String season; //문자열을 저장할 필드 private Season(String season){ //생성자 (싱글톤) this.season = season; } public String getSeason(){ //Getter return season; } }
abstract class Season{ public static final Seson SPRING = new Season("봄"); public static final Seson SUMMER = new Season("여름"); public static final Seson FALL = new Season("가을"); public static final Seson Winter = new Season("겨울"); private String season; private Season(String season){ this.season = season; } public String getSeason(){ return season; } }
- Multiton 이란 여러 개의 관리되는 인스턴스를 가지는 클래스를 말함(2)
enum Operation{ PLUS("+"){ @Override public double apply(double x, double y){ return x+y; } }; private final String symbol; Operation(String symbol){ this.symbol = symbol; } public abstract double apply(double x, double y); }
abstract class Operation{ public static final Operation PLUS = new Operation("+"){ @Override public double apply(double x, double y){ return x+y; } }; private final String symbol; Operation(String symbol){ this.symbol = symbol; } public abstract double apply(double x, double y); }
- Multiton 이란 여러 개의 관리되는 인스턴스를 가지는 클래스를 말함(1)
- Enum 을 이용하면 싱글톤 객체를 만들 수 있음
- 예시 - 도형에 대한 팩토리 메소드 패턴
- Enum 을 활용한 해결 아이디어
- 팩토리 서브 클래스 추가 대신에, Enum 변수 추가로 대체 가능
- 팩토리 객체를 외부에서 만들 필요가 없음
enum EnumShapeFactory{ RECTANGLE{ @Override public Shape createShape(){ return new Rectangle(); } }, CIRCLE{ @Override public Shape createShape(){ return new Circle(); } }; public Shape create(String color){ Shape shape = createShape(); shape.setColor(color); return shape; } abstract protected Shape createShape(); } class Client{ public static void main(String[] args){ Shape rectangle = EnumShapeFactory.RECTANGLE.create("red"); rectangle.draw(); Shape circle = EnumShapeFactory.CIRCLE.create("yellow"); circle.draw(); } }
- Enum 을 활용한 해결 아이디어
- Discussion on Enum Factory Method Pattern
- JAVA Enum 의 기능을 활용하여 기존의 Factory Method Pattern 이 가지는 문제 해결
- 새로운 제품(product) 를 추가할 때는 Product class 를 추가해주고 Enum Factory 에 Factory 객체를 추가 해주면 됨
- 하나의 Factory 객체는 싱글톤으로 공유되고, 외부에서 객체를 만들어 줄 필요가 없음 (객체 생성에 따른 부담 완화)
- 새로운 Factory 추가 시 기존의 Enum 코드가 수정되는 단점이 존재
- 결국 코드 구조의 복잡성과 OCP 를 얼마나 엄밀하게 지킬 것인가에 대한 trade-off 가 존재
- 만일 Factory 가 복잡한 상속 계층으로 구성된다면, Enum 외 클래스 상속이 안되기 때문에 상속 구조 표현에 한계가 있음
- JAVA Enum 의 기능을 활용하여 기존의 Factory Method Pattern 이 가지는 문제 해결
- 추가적인 팩토리 클래스를 만들지 않아야 하면서 동시에 단일 팩토리로 제품 객체를 생성할 수 있어야 함
반응형
'3학년 2학기 학사 > 소프트웨어 분석 및 설계' 카테고리의 다른 글
[소프트웨어 분석 및 설계] L19. Facade Pattern, Flyweight Pattern, Proxy Pattern (0) | 2024.12.13 |
---|---|
[소프트웨어 분석 및 설계] L18. Composite Pattern, Decorator Pattern (0) | 2024.12.13 |
[소프트웨어 분석 및 설계] L17. Adapter Pattern, Bridge Pattern (3) | 2024.12.13 |
[소프트웨어 분석 및 설계] L16. Abstract Factory Pattern, Builder Pattern, Prototype Pattern (0) | 2024.12.13 |
[소프트웨어 분석 및 설계] # L14. 디자인 패턴, 싱글톤 패턴 (0) | 2024.12.11 |