ComputerScience,Engineering/디자인패턴

1.B) Creational Pattern - Object

TimeSave 2022. 9. 5. 19:49

 

1.B.1 Abstract Factory(interface - implements)

  • 연관된 서브 클래스를 특정 그룹으로 묶어 한번에 교체하는 패턴
  • 어떤경우에 사용?
    • 특정 라이브러리를 배포하는데 OS별로 지원하는 기능이 상이한 경우 
    • 추상 팩토리 패턴을 사용해 OS별 기능 변경을 통합적으로 변경

 

  • 팩토리 메소드 패턴과 유사하면서도 사용 용도가 다르다는 것을 알 수 있습니다.
  • 물론 추상 팩토리 패턴에 팩토리 메소드 패턴을 차용할 수도 있습니다.
  • 이 포스트에서는 사용하지 않았지만 MachineFactory를 인터페이스가 아닌 추상 클래스(abstract class)로 선언하고 public static MachineFactory getFactory( String arg )등의 메소드를 이용해서 인자값으로 어떤 팩토리를 사용할지 제어 할 수도 있습니다
public interface MachineA {
	public void process();
}
public class MachineA1 implements MachineA {
	@Override
	public void process() {
		System.out.println("execute MachineA1");
	}
}
public class MachineA2 implements MachineA {
	@Override
	public void process() {
		System.out.println("execute MachineA2");
	}
}

 

public interface MachineB {
	public void process();
}
public class MachineB1 implements MachineB {
	@Override
	public void process() {
		System.out.println("execute MachineB1");
	}
}
public class MachineB2 implements MachineB {
	@Override
	public void process() {
		System.out.println("execute MachineB2");
	}
}

 

public interface MachineFactory {
	public MachineA getMachineA();
	public MachineB getMachineB();
}
public class MachineFactoryA implements MachineFactory{
	@Override
	public MachineA getMachineA() {
		return new MachineA1();
	}
	@Override
	public MachineB getMachineB() {
		return new MachineB1();
	}
}
public class MachineFactoryB implements MachineFactory{
	@Override
	public MachineA getMachineA() {
		return new MachineA2();
	}
	@Override
	public MachineB getMachineB() {
		return new MachineB2();
	}
}

 

public class FactoryMain {
	public static void main(String[] args) {
		MachineFactory machineFactoryA = new MachineFactoryA();
		machineFactoryA.getMachineA().process();
		machineFactoryA.getMachineB().process();

		MachineFactory machineFactoryB = new MachineFactoryB();
		machineFactoryB.getMachineA().process();
		machineFactoryB.getMachineB().process();
	}
}

 

 

1.B.2 Builder

  • 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
  • setter 메소드는 존재할 수 없고 오로지 생성자를 통해서만 데이터를 입력 받는다는 가정으로 만든 클래스
  • 어떤경우에 사용? 매개변수가 많을 때
  • 아래 사항을 보강하기 위해, 빌더 패턴을 적용한 Builder 클래스 필수
    • 불필요한 생성자를 만들지 않고 객체를 만든다.
    • 데이터의 순서에 상관 없이 객체를 만들어 낸다.
    • 사용자가 봤을때 명시적이고 이해할 수 있어야 한다.
public class PersonInfoBuilder {
    private String name;
    private Integer age;
    private String favoriteColor;
    private String favoriteAnimal;
    private Integer favoriteNumber;

    public PersonInfoBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public PersonInfoBuilder setAge(Integer age) {
        this.age = age;
        return this;
    }

    public PersonInfoBuilder setFavoriteColor(String favoriteColor) {
        this.favoriteColor = favoriteColor;
        return this;
    }

    public PersonInfoBuilder setFavoriteAnimal(String favoriteAnimal) {
        this.favoriteAnimal = favoriteAnimal;
        return this;
    }

    public PersonInfoBuilder setFavoriteNumber(Integer favoriteNumber) {
        this.favoriteNumber = favoriteNumber;
        return this;
    }

    public PersonInfo build(){
        PersonInfo personInfo = new PersonInfo(name, age, favoriteColor, favoriteAnimal, favoriteNumber);
        return personInfo;
    }
}

 

public class BuilderPattern {
    public static void main(String[] args) {
        // 빌더 객체를 하나 만듭니다.
        PersonInfoBuilder personInfoBuilder = new PersonInfoBuilder();
        // 빌더 객체에 원하는 데이터를 입력합니다. 순서는 상관 없습니다.
        PersonInfo result = personInfoBuilder
                .setName("MISTAKE")
                .setAge(20)
                .setFavoriteAnimal("cat")
                .setFavoriteColor("black")
                .setName("JDM") // 다시 같은 메소드를 호출한다면 나중에 호출한 값이 들어갑니다.
                .setFavoriteNumber(7)
                // 마지막에 .build() 메소드를 호출해서 최종적인 결과물을 만들어 반환합니다.
                .build();
        // print is "name:JDM, age:20, favoriteColor:black, favoriteAnimal:cat, favoriteNumber:7"
        System.out.println(result.getPersonInfo());
    }
}

클래스 내부에 빌더 클래스를 포함 하여 Static 선언해주고  빌더 클래스를 반환 해주면 아래처럼 사용할 수도 있다.

PersonInfo p = PersonInfo.Builder().setName("JDM").setAge(20).build();
/* PersionInfo.java */
public static PersonInfoBuilder Builder(){
    return new PersonInfoBuilder();
}

 

 

1.B.3 ProtoType

  • Original 객체를 새로운 객체에 복사하여 우리의 필요에 따라 수정하는 메커니즘
  • 코드 내에서 객체 창조자(creator)를 서브클래스(subclass)하는 것 회피
  • 생성할 객체 타입을 인스턴스로 결정한다. 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다.
  • clone()을 사용.

 

 

어떤경우에 사용?

  • 적은 리소스로 많은 객체를 생성해야 하는경우 or 객체를 생성하는 데 고비용(시간과 자원)
  • 동일한 객체 생성으로 매번 new 키워드 사용하는것은 자원소모 중복
  • 깊은 복사를 활용한 프로토 타입 패턴 ->  인스턴스화 과정을 거치지 않고 객체를 복제하여 생성
  • 인스턴스화를 통해 생성 로직에 소모되는 처리시간과 자원을 아낄수 있다.
  • 전제조건으로, 비슷한 객체가 이미 있는 경우에 사용

 

 

  • 사용예시
    • ex) 코드가 복사해야 하는 구현 클래스에 의존하지 않아야 하는 경우
      •  -> 이 경우는 코드가 인터페이스를 통해 써드파티 코드와 함께 작동하는 case
    • ex) 객체를 초기화 하는 방식만 다를뿐 서브클래스의 수를 줄이려는 경우

 

public class User implements Cloneable{		//깊은 복사를 위해 Cloneable implements.
    private String name;        //유저 이름
    private String job;         //유저 직업
    private int age;            //유저 나이

    /* User 클래스의 생성자 */
    User(String name, String job, int age) {
        this.name = name;
        this.job = job;
        this.age = age;
    }

    /* User 클래스 내 맴버변수들의 setter */
    public void setName(String name) { this.name = name; }
    public void setJob(String job) { this.job = job; }
    public void setAge(int age) { this.age = age; }

    /* User 클래스 내 맴버변수들의 getter */
    public String getName() { return this.name; }
    public String getJob() { return this.job; }
    public int getAge() { return this.age; }
    
    /* 깊은 복사를 위해 Clone() 재정의 */
    @Override
    public Object clone() throws CloneNotSupportedException {
        User user = (User)super.clone();
        return user;
    }
}
public class ProtoTypePattern {
    public static void main(String[] args) throws CloneNotSupportedException {
        User user1 = new User("수지", "Engineer", 26);
        System.out.println("user1"
                + ", 이름 : " + user1.getName()
                + ", 직업 : " + user1.getJob()
                + ", 나이 : " + user1.getAge());

        /* 깊은 복사의 예 */
        User user2 = (User)user1.clone();         //clone 을 통한 깊은 복사 진행.
        System.out.println("user2"
                + ", 이름 : " + user2.getName()
                + ", 직업 : " + user2.getJob()
                + ", 나이 : " + user2.getAge());

        /* 깊은 복사로 생성된 user2의 데이터 수정 */
        user2.setName("성민");
        user2.setJob("백수");
        user2.setAge(29);
        System.out.println("user2"
                + ", 이름 : " + user2.getName()
                + ", 직업 : " + user2.getJob()
                + ", 나이 : " + user2.getAge());

        /* user2의 수정에도 user1의 데이터는 변동없음 */
        System.out.println("user1"
                + ", 이름 : " + user1.getName()
                + ", 직업 : " + user1.getJob()
                + ", 나이 : " + user1.getAge());
    }
}

 

 

 

1.B.4 Singleton

  • 단 하나의 유일한 객체를 만들기 위한 디자인 패턴
  • 어떤 경우에 사용? : 유일하게 사용해야 할 객체가 필요할때
  • 사용 
    • 생성자 private 을 선언해, 외부에서 new 객체 생성 불가.
    • getInstance 메소드를 호출해서 객체를 받아온다.
    • getInstance는 static 선언을 해준다.
    • getInstance()는 객체가 없다면 새 객체를 만들어 반환하고, 있다면 기존 객체를 반환합니다.

 

public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance(){
        if( instance == null ){ // (1)
            instance = new Singleton(); // (2)
        }
        return instance;
    }
}
Singleton s = new Singleton(); // (X)
Singleton s = Singleton.getInstance(); // (O)
  • 멀티 스레드 문제
    • 멀티쓰레드를 돌리면, 싱글톤인데 여러개 생성될 수가 있다.

  • 해결 1 : synchronized 키워드 붙이기(쓰레드 싱크 맞춰주는 키워드, 성능 매우 저하 됨)
    • 원글 : 행위(메소드)에 대해 동기화하고 volatile 은 객체(인스턴스)에 대해 원자성(atomic)을 보장한다.
  • 해결2 : Eager initialization : 프로그램 시작시 자동 초기화
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

 

  • 해결 3 : SingletonHolder : static class SingletonHolder를 만들어서 홀더에서 다룬다.
public class Singleton {
     private Singleton(){}

     private static class SingletonHolder {
             private static final Singleton INSTANCE = new Singleton();
     }

     public static Singleton getInstance() {
             return SingletonHolder.INSTANCE;
     }
}

 

 

 

 

 

 

[디자인패턴/Design Patter] Prototype 패턴 프로토타입 패턴 (tistory.com)

 

[디자인패턴/Design Patter] Prototype 패턴 프로토타입 패턴

관련 내용은 [자바 언어로 배우는 디자인 패턴 입문] 이라는 책의 공부 내용을 개인적으로 정리한 내용입니다. 처음 배우는 부분이기 때문에 틀린 부분이 있다면 지적해주시면 감사하겠습니다.

lee1535.tistory.com

 

 

[디자인 패턴의 정석] 생성패턴 - 프로토 타입 패턴 (Prototype) (tistory.com)

 

[디자인 패턴의 정석] 생성패턴 - 프로토 타입 패턴 (Prototype)

개발자 yeah의 Developer Story - 디자인 패턴 오늘은 저번 포스팅에 이어서 프로토 타입 패턴 을 이야기해보려 합니다. 먼저 프로토 타입 패턴 설명에 앞서 객체 생성에 대해 간단히 설명하도록

yeah.tistory.com

프로토타입 패턴 (Prototype Pattern) | keencho's blog

 

프로토타입 패턴 (Prototype Pattern)

프로토타입 패턴 프로토타입 패턴은 원형이 되는 인스턴스를 사용해 새롭게 생성할 객체의 종류를 명시하여 새로운 객체가 생성될 시점에 인스턴스의 타입이 결정되도록 하는 패턴입니다.

keencho.github.io

 

 

빌더 패턴(Builder Pattern) :: JDM's Blog

 

빌더 패턴(Builder Pattern) :: JDM's Blog

간만에 작성하는 디자인 패턴 포스트입니다. 이번 포스트에서는 빌더 패턴Builder Pattern에 대해 알아보고자 합니다. Builder Pattern 빌더 패턴은 추상 팩토리 패턴이나 팩토리 메소드 패턴과는 조금

jdm.kr

추상 팩토리 패턴(Abstract Factory Pattern) :: JDM's Blog

 

추상 팩토리 패턴(Abstract Factory Pattern) :: JDM's Blog

간만에 돌아오는 디자인 패턴 포스트입니다. 이번엔 추상 팩토리 패턴(Abstract Factory Pattern)에 대해 알아보고자 합니다. Overview 추상 팩토리 패턴은 많은 수의 연관된 서브 클래스를 특정 그룹으로

jdm.kr

https://preamtree.tistory.com/135

 

생성자의 매개변수가 많을 때 쓰면 좋다 - Builder Pattern(빌더 패턴)

1. 빌더 패턴이란?  빌더 패턴(Builder Pattern)은 추상 팩토리 패턴(Abstract Factory Pattern)과 팩토리 메소드 패턴(Factory Method Pattern)과 함께 객체의 생성과 관련된 디자인 패턴이다.  빌더 패턴은 생..

preamtree.tistory.com