개발/디자인패턴

[Java][디자인 패턴] 6. 프로토타입 패턴 (Prototype Pattern)

nova_dev 2022. 3. 5. 23:59
반응형

디자인패턴
[Java][디자인 패턴] 6. 프로토타입 패턴 (Prototype Pattern)

프로토타입 패턴은 new 키워드를 사용하지 않고 객체를 복제해 생성하는 패턴이다.

 프로토타입 패턴이란?

  • new 키워드를 통해 객체를 생성하는데 비용이 많이 든다면 프로토타입 패턴을 통해 객체를 복제하는 것도 방법이 될 수 있다.
  • 객체를 복제하면 인스턴스화 과정이 생략되어 생성 로직에 소모되는 처리 비용과 자원을 절약할 수 있다.

 프로토타입 패턴 구조

복제할 수 있는 개발자 클래스

한 개발자를 생성해서 스킬을 업데이트하고 개발 언어와 연차를 업그레이드할 수 있는 메서드가 있다고 해보자.
우선 생성자에서는 개발자의 이름과 연차(1년 차)를 초기화하고, Languege, Skill은 빈 HashMap으로 초기화되어있다.
이 상황에서 차곡차곡 upgradeLanguage, upgradeSkill 등의 메서드를 통해 해당 개발자를 발전시킨다. 이렇게 개발자의 언어와 스킬을 향상하고 연차를 올리는데 상당히 많은 처리 비용과 리소스가 투입된다.
그런데 만약 이 개발자를 복사할 수 있다면? 그냥 연차와 배운 스킬, 언어들을 그대로 복제할 수 있다면 이런 처리 비용을 절약할 수 있을 것이다. (실제로는 이런 게 불가능해서 다행이다. 내 밥줄이 살아있어서)

생각해본 예시는 위와 같지만, 실제 서비스 개발에 적용한다면 DB에서 데이터를 가져와서 이것저것 조합해서 생성해야 하는데 비용이 많이 들어서 해당 객체의 값만 복사하는 것이 낫다거나 생성자나 객체의 특정 값을 세팅하는데 계산량이 너무 많다거나 하는 케이스에 응용해볼 법하지 않을까 싶다. (아직 해당 패턴을 직접 적용해본 경험은 없는데 코드를 구경하면 가끔 Cloneable을 구현한 케이스는 본 적이 있다.)

해당 패턴을 구현하다 보니 Effective Java에서 얘기했던, clone 재정의를 주의하라던 내용이 머리를 스쳐 지나가서 잠깐 링크를 걸어두었다. 시간 나면 한번 읽어보면 좋을 것 같다. (깨알 같은 블로그 홍보)
참고 자료: Effective Java 13강: clone 재정의는 주의해서 진행하라 - https://hirlawldo.tistory.com/63

 프로토타입 패턴 코드

1. Developer 클래스
개발자를 한번 양성해본다.
개발자를 초기화하는 경우 이름만 넣고 초기화를 한다. 차곡차곡 upgradeLanguage, upgradeSkill 메서드를 통해서 스킬을 추가하거나 레벨을 올릴 수 있고 Cloneable을 구현하고 있어서 clone 메서드를 통해 해당 Developer를 복제할 수도 있다.

public class Developer implements Cloneable {
    private String name;
    private Integer workingYear; // 연차...
    private Map<String, Integer> languageMap;
    private Map<String, Integer> skillMap;

    public Developer(String name) {
        this.name = name;
        this.workingYear = 1;
        this.languageMap = new HashMap<>();
        this.skillMap = new HashMap<>();
    }

    public Developer(String name, Integer workingYear, Map<String, Integer> languageMap, Map<String, Integer> skillMap) {
        this.name = name;
        this.workingYear = workingYear;
        this.languageMap = languageMap;
        this.skillMap = skillMap;
    }

    public void upgradeLanguage(String language) {
        languageMap.put(language, languageMap.getOrDefault(language, 0) + 1);
    }

    public void upgradeSkill(String skill) {
        skillMap.put(skill, skillMap.getOrDefault(skill, 0) + 1);
    }

    public void upgradeWorkingYear() {
        this.workingYear++;
    }

    public String getName() {
        return name;
    }

    public Integer getWorkingYear() {
        return workingYear;
    }

    public Map<String, Integer> getLanguageMap() {
        return languageMap;
    }

    public Map<String, Integer> getSkillMap() {
        return skillMap;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        String name = this.name + "_clone";
        Integer workingYear = this.workingYear;
        Map<String, Integer> skillMap = new HashMap<>();
        for (String key : this.skillMap.keySet()) {
            skillMap.put(key, this.skillMap.get(key));
        }
        Map<String, Integer> languageMap = new HashMap<>();
        for (String key : this.languageMap.keySet()) {
            languageMap.put(key, this.languageMap.get(key));
        }

        return new Developer(name, workingYear, languageMap, skillMap);
    }

    @Override
    public String toString() {
        return "Developer{" +
                "name='" + name + '\'' +
                ", workingYear=" + workingYear +
                ", languageMap=" + languageMap +
                ", skillMap=" + skillMap +
                '}';
    }
}

2. 개발자를 양성하고 그대로 복제하는 테스트 코드

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class DeveloperTest {
    @Test
    @DisplayName("개발자를 신입부터 양성하고 그대로 복사한다.")
    void makeDeveloper() throws CloneNotSupportedException {
        Developer developer = new Developer("신입개발자");
        developer.upgradeWorkingYear();
        developer.upgradeLanguage("자바"); // 만약 업그레이드하는데 시간이 소모된다면??
        developer.upgradeSkill("프레임워크");
        developer.upgradeSkill("데이터베이스");
        developer.upgradeSkill("리눅스");
        developer.upgradeSkill("클라우드");
        developer.upgradeLanguage("자바");

        Developer cloneDeveloper = developer.clone();
        System.out.println(developer);
        System.out.println(cloneDeveloper);
    }
}

 

 

 프로토타입 패턴의 특징

  1. 생성자 동작 처리 배제
  2. 적은 자원 소모로 객체 대량 생산
  3. 유사 객체 사용 시 유용
  4. 기존 객체 생성 방법을 몰라도 생성 가능

 Github 코드

Github 예제 코드 링크

Github 예제 테스트 코드 링크

참고 자료

 

 

 

 

 

반응형