티스토리 뷰

반응형

1. JavaEffective

장점

1.  이름을 가질수 있다.  반환될 객체의 특성을 쉽게 묘사할 수 있다.
Enter(int, int, Random) 과 Enter.minimumLevel 중 어느 것이 더 잘 설명이 되는지 생각해보자.

2. 호출 될 때마다 인스턴스를 새로 생성하지 않아도 된다. 

인스턴스를 미리 만들어 놓거나 새로 생성한 인스턴스를 캐싱하여 재활용하는 식으로 불필요한 객체 생성을 피할 수 있다.( 싱글 톤, 인스턴스화 불가)

3.  반환 타입의 하위 타입 객체를 반환할 수 있는 능력 
구현 클래스를 공개하지 않고도 그 객체를 반환할 수 있다.

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

 

단점

1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
상속보다 컴포지션(자바의 정석에서는 Has a 상속이라고도 한다.)을 사용하도록 유도하고 불변 타입으로 만들려면 이 제약을 지켜야 한다는 점에서 오히려 장점으로 받아들일 수도 있다.

2. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다. 
API 설명에 명확히 드러나지 않으니 사용자는 정적 팩토리 메서드 방식 클래스를 인스터스화할 방법을 알아내야 한다. 이를 해결할 방법 중 하나는 네이밍 컨벤션을 잘 따르도록 하자.

 

 

1. from: 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
EX> Date d  = Date.from(instant);

2. of: 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Optional<CustomObject> data = Optional.of(customObject);

3. valueOf: from과 of의 더 자세한 버전
String number = String.valueOf(1);

4. instance or getInstance: (매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지 않는다.

User user = User.getInstance(Social.GOOGLE);
...

 

예시

public final class iPhone {
    private String color;
    private int storage;
    private boolean isMax;

    public static iPhone valueOf(String color, int storage, boolean isMax){
        return new iPhone(color,storage,isMax);
    }

    private iPhone(String color,int storage, boolean isMax){
        this.color = color;
        this.storage = storage;
        this.isMax = isMax;
    }

    @Override
    public String toString() {
        String max = isMax?"Max":"";
        return "iPhone13 "+max+"-> color : "+color+", storage: "+storage;
    }
}

 

    @Test
    void test_정적팩토리메서드(){
        String color = "Sierra Blue";
        int storage = 128;
        iPhone phone = iPhone.valueOf(color, storage, false);
        System.out.println(phone);
    }

 

2. GoF 디자인 패턴

JavaEffective에서 말한 팩터리 메소드와 약간 다르다는 것을 인지하고 읽기 바란다.

서브 클래스 중 어는 것을 생성해야 하는지에 대한 정보를 캡슐화하고, 그것을 프레임워크에서 떼어내는 형식

활용성
- 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
- 생성할 객체를 기술하는 책임을 자신의 서브클래스가 지정했으면 할 때
- 객체 생성의 책임을 몇 개의 보조 서브클래스 가운데 하나에게 위임하고, 어떤 서브 클래스가 위임지인지에 대한 정보를 국소화시키고 싶을 때

얻는 효과

1. 서브클래스에 대한 hook 메서드를 제공 
팩터리 메서드로 클래스 내부에서 객체를 생성하는 것이 객체를 직접 생성하는 것보다 훨씬 응용성이 높아진다. 팩터리 메서드 패턴에서는 객체별로 서로 다른 버전을 제공하는 훅기능을 서브클래스에 정의

2. 병렬적인 클래스 계통을 연결하는 역할을 담당

병렬적 클래스 계통은 클래스가 자신의 책임을 분리된 다른 클래스에 위임할 때 발생

구현 방식

1.  인터페이스 또는 추상 클래스를 정의하고, 정의한 팩터리 메서드에 대한 구현은 제공하지 않는 경우
2. 구체클래스로 구현하고, 팩터리 메서드에 대한 기본 구현을 제공하는 경우

그 이외에는 책에 자세히 나와 있다.

 

GoF 디자인 패턴에서 말하는 팩터리 메서드 방법은 다형성을 이용한 방법으로 이해했다.

 

예제를 통해 설명해보겠습니다.

 

1. SmartPhone.java, SmartPhone.py

public interface SmartPhone {
    int getRelease();
    String getOperationSystem();
    int getPrice();
}

 

import abc


class SmartPhone(object, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def get_release(self):
        raise NotImplementedError('must be define get_release() method')

    @abc.abstractmethod
    def get_os(self):
        raise NotImplementedError('must be define get_os() method')

    @abc.abstractmethod
    def get_price(self):
        raise NotImplementedError('must be define get_price() method')

 

2. Samsung.java, Samsung.py

public class Samsung implements SmartPhone{
    private int year;
    private String os;
    private int price;
    private String flagShip;

    public Samsung(){
        this.year = 2021;
        this.os = "ANDROID";
        this.price = 1000000;
        this.flagShip = "FLIP";

    }

    public Samsung(int year, String os, int price, String flagShip){
        this.year = year;
        this.os = os;
        this.price = price;
        this.flagShip = flagShip;
    }

    @Override
    public int getRelease() {
        return year;
    }

    @Override
    public String getOperationSystem() {
        return os;
    }

    @Override
    public int getPrice() {
        return price;
    }

    public String getFlagShip(){
        return flagShip;
    }
}

 

from SmartPhone import SmartPhone


class Samsung(SmartPhone):
    def __init__(self, flag_ship: str = 'FLIP', year: int = 2021, os: str = 'ANDROID', price: int = 1000000):
        self.year = year
        self.os = os
        self.price = price
        self.flag_ship = flag_ship

    def __str__(self):
        return f'갤럭시 {self.flag_ship} 출시년도는 {self.year} 이고 운영체제는 {self.os} 그리고 가격은 {self.price} 입니다'

    def get_release(self):
        return self.year

    def get_os(self):
        return self.os

    def get_price(self):
        return self.price

    def get_flag_ship(self):
        return self.flag_ship

 

3. Apple.java, Apple.py

public class Apple implements SmartPhone{
    private int year;
    private String os;
    private int price;
    private String spec;

    public Apple(){
        this.year = 2021;
        this.os = "IOS";
        this.price = 1200000;
        this.spec = "PRO";

    }

    public Apple(int year, String os, int price, String spec){
        this.year = year;
        this.os = os;
        this.price = price;
        this.spec = spec;
    }

    @Override
    public int getRelease() {
        return year;
    }

    @Override
    public String getOperationSystem() {
        return os;
    }

    @Override
    public int getPrice() {
        return price;
    }

    public String getSpec(){
        return spec;
    }
}

 

from SmartPhone import SmartPhone


class Apple(SmartPhone):
    def __init__(self, spec: str = 'PRO', year: int = 2021, os: str = 'IOS', price: int = 1200000):
        self.year = year
        self.os = os
        self.price = price
        self.spec = spec

    def __str__(self):
        return f'iPhone13 {self.spec} 출시년도는 {self.year} 이고 운영체제는 {self.os} 그리고 가격은 {self.price} 입니다'

    def get_release(self):
        return self.year

    def get_os(self):
        return self.os

    def get_price(self):
        return self.price

    def get_spec(self):
        return self.spec

 

3. EtcSmartPhone.java, EtcSmartPhone.py

public class EtcSmartPhone implements SmartPhone{
    @Override
    public int getRelease() {
        return 0;
    }

    @Override
    public String getOperationSystem() {
        return null;
    }

    @Override
    public int getPrice() {
        return 0;
    }
}

 

from SmartPhone import SmartPhone


class EtcSmartPhone(SmartPhone):

    def __str__(self):
        return '잡폰입니다.'

    def get_os(self):
        return None

    def get_price(self):
        return 0

    def get_release(self):
        return 0

 

기본적으로 SmartPhone 인터페이스로 구현한 클래스 3개를 만들어 놨다.

 

 

4. Shop.java, Shop.py

public interface Shop {
    SmartPhone createSmartPhone(String company);
}

 

import abc


class Shop(object, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def create_smartphone(self, company:str):
        raise NotImplementedError('must be define create_smartphone method')

5. CouPang.java, CouPang.py

public class CouPang implements Shop{
    @Override
    public SmartPhone createSmartPhone(String company) {
        if (company.equals("SAMSUNG")){
            return new Samsung();
        }else if(company.equals("APPLE")){
            return new Apple();
        }else{
            return new EtcSmartPhone();
        }
    }
}

 

from EtcSmartPhone import EtcSmartPhone
from Samsung import Samsung
from Shop import Shop
from Apple import Apple


class CouPang(Shop):

    def create_smartphone(self, company: str):
        if company == 'SAMSUNG':
            return Samsung()
        elif company == 'APPLE':
            return Apple()
        else:
            return EtcSmartPhone()

 

쿠팡 클래스에서 식별자를 받고 식별자에 따라 각각의 스마트폰을 만들어 주는 방식이 GoF 디자인패턴에서 설명하는 팩터리 메서드라고 이해하면 된다.

 

파이썬은 굳이 인터페이스를 구현해서 할 필요는 없지만 자바와 비슷하게 구현해서 비교하고자 했습니다.

 

혹시 설명이 잘못 되었거나 보충할 내용이 있으면 말씀해주시길 바랍니다.

끝까지 읽어 주셔서 감사합니다.

 

참고: GoF 디자인 패턴, JavaEffective

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함