Design Pattern | Abstract Factory Methods


Abstract Factory 패턴이란?

  • Abstract Factory는 추상 공장 이라는 의미한다.
  • 추상적인 것은 구체적으로 어떻게 구현되고 있는지에 대해서는 고려하지 않고, 인터페이스에만 주목하고 있는 상태이다.
  • Abstract Factory 패턴은 부품의 구체적인 구현에 주목하지 않고 인터페이스에 주목한다. 그리고 그 인터페이스만을 사용해 부품을 조립하여 제품에 정리하는 방식이다.
  • 관련 부품을 조합하여 제품 만든 것이다.
  • GoF 디자인 패턴에서는 생성과 관련된 디자인 패턴으로 분류된다.

Abstract Factory 패턴 예제 프로그램

즐겨찾기 목록을 HTML 형식으로 출력하는 예제 프로그램이다.

Class Diagram
Abstract Factory Pattern Class Diagram

1. Factory 클래스

추상 공장을 대표하는 클래스이다. Link, Tray, Page를 만든다.

Factory.java

package com.devkuma.designpattern.creational.abstractfactory.factory;

public abstract class Factory {

    public abstract Link createLink(String caption, String url);

    public abstract Tray createTray(String caption);

    public abstract Page createPage(String title);

    public static Factory getFactory(String classname) {
        Factory factory = null;
        try {
            factory = (Factory) Class.forName(classname).getDeclaredConstructor().newInstance();
        } catch (ClassNotFoundException e) {
            System.err.println("클래스 " + classname + "를 찾을 수 없습니다.");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }
}

2. Item 클래스

Link와 Tray를 통일적으로 취급하는 클래스이다.

Item.java

package com.devkuma.designpattern.creational.abstractfactory.factory;

public abstract class Item {
    protected String caption;

    public Item(String caption) {
        this.caption = caption;
    }

    public abstract String makeHTML();
}

추상 부품: HTML 링크를 나타내는 클래스이다.

Link.java

package com.devkuma.designpattern.creational.abstractfactory.factory;

public abstract class Link extends Item {
    protected String url;

    public Link(String caption, String url) {
        super(caption);
        this.url = url;
    }
}

4. Tray 클래스

추상 부품: Link와 Tray를 모은 클래스이다.

Tray.java

package com.devkuma.designpattern.creational.abstractfactory.factory;

import java.util.ArrayList;

public abstract class Tray extends Item {
    protected ArrayList tray = new ArrayList();

    public Tray(String caption) {
        super(caption);
    }

    public void add(Item item) {
        tray.add(item);
    }
}

5. Page 클래스

추상 파트: HTML 페이지를 나타내는 클래스이다.

Page.java

package com.devkuma.designpattern.creational.abstractfactory.factory;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;

public abstract class Page {
    protected String title;
    protected ArrayList content = new ArrayList();

    public Page(String title) {
        this.title = title;
    }

    public void add(Item item) {
        content.add(item);
    }

    public void output() {
        try {
            String filename = title + ".html";
            Writer writer = new FileWriter(filename);
            writer.write(this.makeHTML());
            writer.close();
            System.out.println(filename + "을 생성했습니다.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public abstract String makeHTML();
}

6. ListFactory 클래스

구체적인 공장을 나타내는 클래스이다. ListLink, ListTray, ListPage를 만든다.

ListFactory.java

package com.devkuma.designpattern.creational.abstractfactory.listfactory;

import com.devkuma.designpattern.creational.abstractfactory.factory.Factory;
import com.devkuma.designpattern.creational.abstractfactory.factory.Link;
import com.devkuma.designpattern.creational.abstractfactory.factory.Page;
import com.devkuma.designpattern.creational.abstractfactory.factory.Tray;

public class ListFactory extends Factory {
    public Link createLink(String caption, String url) {
        return new ListLink(caption, url);
    }

    public Tray createTray(String caption) {
        return new ListTray(caption);
    }

    public Page createPage(String title) {
        return new ListPage(title);
    }
}

구체적인 파트: HTML 링크를 나타내는 클래스이다.

ListLink.java

package com.devkuma.designpattern.creational.abstractfactory.listfactory;

import com.devkuma.designpattern.creational.abstractfactory.factory.Link;

public class ListLink extends Link {
    public ListLink(String caption, String url) {
        super(caption, url);
    }

    public String makeHTML() {
        return "  <li><a href=\"" + url + "\">" + caption + "</a></li>\n";
    }
}

8. ListTray 클래스

구체적인 부품:Link나 Tray를 모은 클래스이다.

ListTray.java

package com.devkuma.designpattern.creational.abstractfactory.listfactory;

import com.devkuma.designpattern.creational.abstractfactory.factory.Item;
import com.devkuma.designpattern.creational.abstractfactory.factory.Tray;

import java.util.Iterator;

public class ListTray extends Tray {
    public ListTray(String caption) {
        super(caption);
    }

    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<li>\n");
        buffer.append(caption + "\n");
        buffer.append("<ul>\n");
        Iterator it = tray.iterator();
        while (it.hasNext()) {
            Item item = (Item) it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("</li>\n");
        return buffer.toString();
    }
}

9. ListPage 클래스

구체적인 파트: HTML 페이지를 나타내는 클래스이다.

ListPage.java

package com.devkuma.designpattern.creational.abstractfactory.listfactory;

import com.devkuma.designpattern.creational.abstractfactory.factory.Item;
import com.devkuma.designpattern.creational.abstractfactory.factory.Page;

import java.util.Iterator;

public class ListPage extends Page {
    public ListPage(String title) {
        super(title);
    }

    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<html><head><title>" + title + "</title></head>\n");
        buffer.append("<body>\n");
        buffer.append("<h1>" + title + "</h1>\n");
        buffer.append("<ul>\n");
        Iterator it = content.iterator();
        while (it.hasNext()) {
            Item item = (Item) it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("</body></html>\n");
        return buffer.toString();
    }
}

10. Main 클래스

메인 처리를 실행하는 클래스이다.

Main.java

package com.devkuma.designpattern.creational.abstractfactory;

import com.devkuma.designpattern.creational.abstractfactory.factory.Factory;
import com.devkuma.designpattern.creational.abstractfactory.factory.Link;
import com.devkuma.designpattern.creational.abstractfactory.factory.Page;
import com.devkuma.designpattern.creational.abstractfactory.factory.Tray;

public class Main {
    public static void main(String[] args) {

        Factory factory = Factory.getFactory("com.devkuma.designpattern.creational.abstractfactory.listfactory.ListFactory");

        Link devkuma = factory.createLink("Devkuma", "https://www.devkuma.com//");
        Link araikuma = factory.createLink("araikuma", "https://araikuma.tistory.com/");

        Link naver = factory.createLink("Naver", "https://www.naver.com/");
        Link daum = factory.createLink("Daum", "https://www.daum.com/");
        Link google = factory.createLink("Google", "https://www.google.com/");

        Tray pgTray = factory.createTray("프로그래밍");
        pgTray.add(devkuma);
        pgTray.add(araikuma);

        Tray searchTray = factory.createTray("검색사이트");
        searchTray.add(naver);
        searchTray.add(daum);
        searchTray.add(google);

        Page page = factory.createPage("즐겨찾기");
        page.add(pgTray);
        page.add(searchTray);
        page.output();
    }
}

11. 실행 결과

프로그램으로 생성한 파일을 아래와 같다.

즐겨찾기.html

<html><head><title>즐겨찾기</title></head>
<body>
<h1>즐겨찾기</h1>
<ul>
<li>
프로그래밍
<ul>
  <li><a href="https://www.devkuma.com//">Devkuma</a></li>
  <li><a href="https://araikuma.tistory.com/">araikuma</a></li>
</ul>
</li>
<li>
검색사이트
<ul>
  <li><a href="https://www.naver.com/">Naver</a></li>
  <li><a href="https://www.daum.com/">Daum</a></li>
  <li><a href="https://www.google.com/">Google</a></li>
</ul>
</li>
</ul>
</body></html>

Abstract Factory 패턴의 장점

예를 들어, 예제 프로그램에 새로운 구체적인 공장을 추가하는 경우, Factory, Link, Tray, Page의 서브 클래스 만들어 각각의 추상 메소드를 구현하게 된다.
즉, factory 패키지의 클래스가 가지고 있는 추상 부분을 구체화해 가는 것만이 된다. 이 때 아무리 구체적인 공장을 추가해도 추상 공장을 수정할 필요가 없다.