【设计模式】工厂方法和抽象工厂

一、前言

依然记得几年前面试被问到工厂方法模式和抽象工厂有什么区别时,我一脸懵逼哑口无言。本文就分别探讨下这两种设计模式。

二、Factory Method

工厂方法(Factory Method)模式,将实例的生成交给子类,父类决定实例的生成方式,但不决定所要生成的具体的类。
这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。

工厂方法的类图如下:

enter description here
enter description here

Product类和Factory类都是抽象类,属于框架部分,IDCard和IDCardFactory分别为其子类。

Factory类

public abstract class Factory {

    /**
     * 这里用到了 模板方法模式
     * 使用final修饰 防止被子类重写
     * @param owner
     * @return
     */
    public final Product create(String owner){
        Product product = createProduct(owner);
        registerProduct(product);

        return product;
    }

    protected abstract Product createProduct(String owner);

    protected abstract void registerProduct(Product product);
}

该类的create方法定义了生产产品的算法,用到了Template Method模式,只要是Factory Method模式在生成实例时一定会用到Template Method模式。

Product类

public abstract class Product {

    public abstract void use();
}

该类用于定义产品的方法和属性

IDCard

public class IDCard extends Product {

    private String owner;

    /**
     * 不用public修饰防止被new
     * @param owner
     */
    IDCard(String owner){
        System.out.println("制作"+ owner + "的ID卡");
        this.owner = owner;
    }

    @Override
    public void use() {
        System.out.println("使用"+ owner + "的ID卡");
    }

    public String getOwner() {
        return owner;
    }


}

这里构造方法不是public是为了防止被new直接创建实例,只能通过IDCardFactory创建。

IDCardFactory类

public class IDCardFactory extends Factory {

    private List<String> owners = new ArrayList<>();

    /**
     * 通过生成IDCard实例来生产产品
     * @param owner
     * @return
     */
    @Override
    protected Product createProduct(String owner) {

        return new IDCard(owner);
    }

    /**
     * 通过将IDCard的owner保存到owners来注册产品
     * @param product
     */
    @Override
    protected void registerProduct(Product product) {
        owners.add(((IDCard) product).getOwner());
    }
}

测试类Main

public class Main {

    public static void main(String[] args) {
        Factory factory = new IDCardFactory();
        Product card1 = factory.create("小明");
        Product card2 = factory.create("小红");
        Product card3 = factory.create("小刚");

        card1.use();
        card2.use();
        card3.use();
    }
}

运行结果如下:

制作小明的ID卡
制作小红的ID卡
制作小刚的ID卡
使用小明的ID卡
使用小红的ID卡
使用小刚的ID卡

可以看到调用方Main类只用到了框架部分的类(Product和Factory),而不依赖其具体实现,这样当要生成其他类时,框架部分的代码不用修改。

三、Abstract Factory

抽象工厂模式(Abstract Factory),抽象工厂的作用是将“抽象零件”组装为“抽象产品”。
下面的一个示例程序是利用抽象工厂模式,生成如下的网页文件LinkPage.html

enter description here
enter description here

程序的类图如下:

类图
类图

名字 说明
Item 方便统一处理Link和Tray的类
Factory 表示抽象工厂的类(制作Link,Tray,Page)
Link 抽象零件:表示HTML的链接的类
Tray 抽象零件
Page 抽象零件:表示HTML页面的类
ListFactory 表示具体工厂的类
ListLink 具体零件
ListTray 具体零件
ListPage 具体零件:表示HTML页面的类

Item

  public abstract class Item {
    /**
     * 标题
     */
    protected String caption;

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

    /**
     * 子类来实现
     * @return
     */
    public abstract String makeHtml();
}

Link

public abstract class Link extends Item {

    protected String url;

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

}

Tray

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);
    }

}

Page

public abstract class Page {

    protected String title;

    protected String author;

    protected ArrayList content = new ArrayList();

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

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

    /**
     * 一个简单的Template Method模式的方法
     */
    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();
}

抽象工厂Factory

public abstract class Factory {

    public static Factory getFactory(String className) {
        Factory factory = null;
        try {
            // 通过反射生成实例
            factory = (Factory) Class.forName(className).newInstance();
        } catch (ClassNotFoundException e) {
            System.out.println("没有找到 " + className + " 类");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }


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

    public abstract Tray createTray(String caption);

    public abstract Page createPage(String title, String author);
}

ListLink类

public class ListLink extends Link {

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

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

ListTray

public class ListTray extends Tray {

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

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

ListPage

public class ListPage extends Page {

    public ListPage(String title, String author) {
        super(title, author);
    }

    @Override
    public String makeHtml() {
        StringBuilder builder = new StringBuilder();
        builder.append("<html><head><title>" + title + "</head></title>\n");
        builder.append("<body>\n");
        builder.append("<h1>" + title + "</h1>");
        builder.append("<ul>\n");
        Iterator it = content.iterator();
        while (it.hasNext()) {
            Item item = (Item) it.next();
            builder.append(item.makeHtml());
        }
        builder.append("</ul>\n");
        builder.append("<hr><address>" + author + "</address></hr>");
        builder.append("</body></html>\n");
        return builder.toString();
    }
}

ListFactory

public class ListFactory extends Factory {

    @Override
    public Link createLink(String url, String caption) {
        return new ListLink(url, caption);
    }

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

    @Override
    public Page createPage(String title, String author) {
        return new ListPage(title, author);
    }
}

测试类Main

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

        Factory factory = Factory.getFactory("cn.sp.abstract_factory.listfactory.ListFactory");

        Link people = factory.createLink("http://www.people.com.cn", "人民日报");
        Link gmw = factory.createLink("http://www.gmw.cn", "光明日报");


        Link us_yahoo = factory.createLink("http://www.yahoo.com/", "Yahoo!");
        Link jp_yahoo = factory.createLink("http://www.yahoo.jp/","Yahoo!Japan");
        Link excite = factory.createLink("http://www.excite.com/","Excite");
        Link google = factory.createLink("http://www.google.com/","Google");

        Tray trayNews = factory.createTray("日报");
        trayNews.add(people);
        trayNews.add(gmw);

        Tray trayYahoo = factory.createTray("Yahoo!");
        trayYahoo.add(us_yahoo);
        trayYahoo.add(jp_yahoo);

        Tray search = factory.createTray("检索引擎");
        search.add(trayYahoo);
        search.add(excite);
        search.add(google);

        Page page = factory.createPage("LinkPage","杨文轩");

        page.add(trayNews);
        page.add(search);

        page.output();



    }
}

运行结果得到如下文件

  1. <html><head><title>LinkPage</head></title> 
  2. <body> 
  3. <h1>LinkPage</h1><ul> 
  4. <li> 
  5. 日报 
  6. <ul> 
  7. <li><a href= "http://www.people.com.cn">人民日报</a></li> 
  8. <li><a href= "http://www.gmw.cn">光明日报</a></li> 
  9. </ul> 
  10. </li> 
  11. <li> 
  12. 检索引擎 
  13. <ul> 
  14. <li> 
  15. Yahoo! 
  16. <ul> 
  17. <li><a href= "http://www.yahoo.com/">Yahoo!</a></li> 
  18. <li><a href= "http://www.yahoo.jp/">Yahoo!Japan</a></li> 
  19. </ul> 
  20. </li> 
  21. <li><a href= "http://www.excite.com/">Excite</a></li> 
  22. <li><a href= "http://www.google.com/">Google</a></li> 
  23. </ul> 
  24. </li> 
  25. </ul> 
  26. <hr><address>杨文轩</address></hr></body></html> 
  27.  

假如这里需要增加其他工厂如TableFactory,则只需要添加TableLink,TableTray,TablePage(分别为Link,Tray,Page的子类)即可,顶层的抽象类不需要改变。

所以可以看出抽象工厂模式有如下特点:

  • 易于增加具体的工厂
  • 难以增加新零件

四、总结

它们的区别大概如下:
1.工厂方法只能生成某一种产品,且对应的工厂只有一个。而抽象工厂可以增加其他工厂,产品由很多零件组成,生成各种实例。
2.工厂方法比较简单,抽象工厂模式更加复杂和灵活。
3.工厂方法是通过new创建实例,抽象工厂模式是通过反射创建的。

最后附上完整代码地址

posted @ 2020-04-20 20:21  烟味i  阅读(241)  评论(0编辑  收藏  举报