将关联零件组装成产品-Abstract Factory模式

136 阅读3分钟

1、引入

在先前,已经学过了Factory模式,那么Abstract Factory模式又是什么呢?两者之间有什么关联吗? 在这里,可以作出几点概况

  • 两者都可以调用工厂的相关方法来生成指定产品
  • Factory模式指定了具体子类来生成产品,而Abstract Factory模式只需要关心抽象的接口即可,通过抽象的接口来组装产品。而具体类的调用则是用到了反射。

2、示例

上面的解释仍然很抽象,下面通过实例来解释。我们通过抽象工厂来对具体工厂进行操作,构建出html页面。

2.1、抽象零件

抽象零件的父类Item:

public abstract class Item {
    protected String caption;

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

    public abstract String makeHTML();
}

抽象零件Link:

public abstract class Link extends Item{
    protected String url;
    public Link(String caption,String url) {
        super(caption);
        this.url=url;
    }
}

抽象零件Tray:

public abstract class Tray extends Item{
    protected ArrayList<Item> 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<Item> content=new ArrayList<Item>();

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

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

    public void output() {
        String filename = title+".html";
        try {
            FileWriter fileWriter = new FileWriter(filename);
            fileWriter.write(this.makeHTML());
            fileWriter.close();
            System.out.println(filename+"编写完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public abstract String makeHTML();
}

2.2、抽象工厂

在抽象工厂中实现了getFactory,通过发射获取具体工厂

public abstract class Factory {
    public static Factory getFactory(String classname) {
        Factory factory=null;
        try {
            factory=(Factory) Class.forName(classname).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }
    public abstract Link createLink(String caption,String url);
    public abstract Tray createTray(String caption);
    public abstract Page createPage(String title,String author);
}

2.3、具体零件

Link实现:

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

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

Tray实现:

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

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

Page实现:

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

    @Override
    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>");
        buffer.append("<ul>\n");
        Iterator iterator = content.iterator();
        while (iterator.hasNext()) {
            Item next = (Item) iterator.next();
            buffer.append(next.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("<hr><address>"+author+"</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();
    }
}

2.4、具体工厂

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

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

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

2.5、测试

public class Main {
    public static void main(String[] args) {
        String factoryName="abstractfactory.example.listfactory.ListFactory";
        Factory factory = Factory.getFactory(factoryName);

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

        Link baidu = factory.createLink("百度", "https://www.baidu.com");
        Link excite = factory.createLink("Excite", "https://www.excite.com");
        Link google = factory.createLink("Google", "https://www.google.com");

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

        Tray baiduTray = factory.createTray("百度");
        baiduTray.add(baidu);

        Tray searchTray = factory.createTray("搜索引擎");
        searchTray.add(baidu);
        searchTray.add(google);

        Page page = factory.createPage("LinkPage", "kloein");
        page.add(trayNews);
        page.add(baiduTray);
        page.add(searchTray);
        page.output();
    }
}

需要注意的是,在直接编译此Main文件时会报错(因为没有编译ListFactory类),因此需要在之后ListFactory所在路径,如下所示:

javac abstractfactory\example\Main.java abstractfactory\example\listfactory\ListFactory.java

生成的界面如下:

image.png

3、tips

  • 抽象工厂模式和工厂模式的区别在于抽象工厂有着抽象父类接口,而能够通过反射调用它的子类。即通过父类的抽象API完成对具体子类的调用。
  • 抽象工厂的特点提供了良好的解耦性,当要更换新的产品时,只需要在Main中更改子类的名字即可。
  • 然而抽象工厂在提供全新的功能时,因为提供的父类API不足,需要更改所有的类,会带来较大的工作量。