zoukankan      html  css  js  c++  java
  • 【设计模式】工厂方法和抽象工厂

    一、前言

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

    二、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>
    ";
        }
    }
    

    ListTray

    public class ListTray extends Tray {
    
        public ListTray(String caption){
            super(caption);
        }
    
        @Override
        public String makeHtml() {
            StringBuilder builder = new StringBuilder();
            builder.append("<li>
    ");
            builder.append(caption +"
    ");
            builder.append("<ul>
    ");
            Iterator it = tray.iterator();
            while (it.hasNext()){
                Item item = (Item) it.next();
                builder.append(item.makeHtml());
            }
            builder.append("</ul>
    ");
            builder.append("</li>
    ");
            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>
    ");
            builder.append("<body>
    ");
            builder.append("<h1>" + title + "</h1>");
            builder.append("<ul>
    ");
            Iterator it = content.iterator();
            while (it.hasNext()) {
                Item item = (Item) it.next();
                builder.append(item.makeHtml());
            }
            builder.append("</ul>
    ");
            builder.append("<hr><address>" + author + "</address></hr>");
            builder.append("</body></html>
    ");
            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创建实例,抽象工厂模式是通过反射创建的。

    最后附上完整代码地址

  • 相关阅读:
    LeetCode
    已知二叉树的先序遍历和中序遍历序列求后序遍历序列
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    TCP协议的基本规则和在Java中的使用
    Java中UDP协议的基本原理和简单用法
    LeetCode
  • 原文地址:https://www.cnblogs.com/2YSP/p/12740159.html
Copyright © 2011-2022 走看看