5、抽象工厂 abstract factory 将关联组件组成产品 创建型模式

趁热打铁,紧跟着上一节的工厂方法模式。这一节介绍一下抽象工厂模式,以及分析俩个模式的不同

1、何为抽象模式?

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

在Abstract Factory模式中将会出现抽象工厂,它会将抽象零件组装为抽象产品。也就是说,我们并不关心零件的具体实现,而是只关心接口(API )。我们仅使用该接口( API )将零件组装成为产品。

在Tempate Method模式和Builder模式中,子类这- -层负责方法的具体实现。在Abstract Factory模式中也是一样的。在子类这一层中有 具体的工厂,它负责将具体的零件组装成为具体的产品。

我好像听,见有读者在说“关于抽象的话题就此打住吧,赶快让我们看看示例程序”。那么我们 就赶紧来看看下面这段抽象工厂的示例程序吧。跟着代码看抽象模式!

2、示例程序

效果:

image-20200717162354121

类图:

image-20200717162250762

类的一览表:

image-20200717162544348

类的位置:

image-20200717162617884

2.1 定义抽象的零件 Item类

 package cn.design.abstractfactory.factrory;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 9:11
  * @Description Item类是Link类和Tray类的父类(Item有 “ 项目 ” 的意思)。这样 Link类和Tray类就具有可替换性了。
  */
 public abstract class Item {
     /**
      * caption字段表示项目的“标题”。
      */
     protected String caption;
 
     public Item(String caption) {
         this.caption = caption;
    }
 
     /**
      * makeHTML方法是抽象方法,需要子类来实现这个方法。该方法会返回HTML文件的内容(需要子类去实现)。
      *
      * @return HTML
      */
     public abstract String makeHTML();
 }
 

2.2 定义抽象的零件组成 Link类

 package cn.design.abstractfactory.factrory;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 9:14
  * @Description Link类(代码清单8 - 2)是抽象地表示HTML的超链接的类。
  * 由于Link类中没有实现父类( Item类)的抽象方法(ma keHTML),因此它也是抽象类。
  */
 public abstract class Link extends Item {
     /**
      * url字段中保存的是超链接所指向的地址。乍一看,在Link类中好像一个抽象方法都没有,
      * 但实际上并非如此。
      */
     protected String url;
 
     public Link(String caption, String url) {
         super(caption);
         this.url = url;
    }
 }
 

2.3 定义抽象的零件组成 Tray类

 package cn.design.abstractfactory.factrory;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 9:18
  * @Description Tray类(代码清单8 - 3)表示的是-一个含有多个Link类和Tray类的容器( Tray有托盘的意
  * 思。请想象成在托盘上放置着-一个- -个项目 )。
  * 虽然Tray类也继承了Item类的抽象方法makeHTML,但它并没有实现该方法。因此,Tray
  * 类也是抽象类。
  */
 public abstract class Tray extends Item {
     protected List<Item> tray = new ArrayList<>();
 
     public Tray(String caption) {
         super(caption);
    }
 
     /**
      * Tray类使用add方法将Link类和Tray类集合在- -起。为了表示集合的对象是“Link类
      * 和Tray类”,我们设置add方法的参数为Link类和Tray类的父类Item类。
      *
      * @param item
      */
     public void add(Item item) {
         tray.add(item);
    }
 
 }
 

2.4 定义抽象的产品 Page 类

 package cn.design.abstractfactory.factrory;
 
 import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 10:43
  * @Description Page类是抽象地表示HTML页面的类。
  * 如果将Link和Tray比喻成抽象的“零件”,那么Page类就是抽象的“产品”。
  */
 public abstract class Page {
     /**
      * title 和author分别是表示页面标题和页面作者的字段。作者名字通过参数传递给Page类的构造函数。
      */
     protected String title;
     protected String author;
     protected List<Item> content = new ArrayList<>();
 
     public Page(String title, String author) {
         this.title = title;
         this.author = author;
    }
 
     /**
      * 可以使用add方法向页面中增加Item(即Link或Tray)。增加的Item将会在页面中显示出来。
      *
      * @param item 零件
      */
     public void add(Item item) {
         content.add(item);
    }
 
     /**
      * output方法首先根据页面标题确定文件名,接着调用ma keHTML方法将自身保存的HTML
      * 内容写人到文件中。
      * 其中,我们可以去掉如下语句(1 )中的this,将其写为如下语句(2 )那样。
      * writer .write (this . makeHTML()) ;
      * ...... ( 1 )
      * writer . write (makeHTML()) ;
      * ...... (2 )
      * 为了强调调用的是Page类自己的makeHTML方法,我们显式地加上了this。这里调用的
      */
     public void output() {
         try {
             String fileName = title + ".html";
             FileWriter writer = new FileWriter(fileName);
             writer.write(this.makeHTML());
             writer.close();
             System.out.println(fileName + " 编写完成");
        } catch (IOException e) {
             e.printStackTrace();
        }
    }
 
     /**
      * makeHTML方法是一- 个抽象方法。output 方法是一个简单的Template Method模式的方法。
      *
      * @return html 字符串
      */
     public abstract String makeHTML();
 
 }
 

2.5 定义抽象的工厂 Factory类

 package cn.design.abstractfactory.factrory;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 11:00
  * @Description TODO
  */
 public abstract class Factory {
     /**
      * getFactory 方法通过调用Class类的forName方法来动态地读取类信息,接着使用,
      * newInstance方法生成该类的实例,并将其作为返回值返回给调用者。
      * Class类属于java.lang包,是用来表示类的类。Class 类包含于Java的标准类库中。
      * forName是java.lang.Class的类方法(静态方法),newInstance则是java. lang.
      * Class的实例方法。
      * 请注意,虽然getFactory方法生成的是具体工厂的实例,但是返回值的类型是抽象工厂类型。
      * @param className
      * @return
      */
     public static Factory getFactory(String className) {
         Factory factory = null;
         try {
             factory = (Factory) Class.forName(className).newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
             e.printStackTrace();
        } catch (ClassNotFoundException e) {
             System.out.println("ClassNotFoundException 没有找到 " + className + " 类.");
        }
         return factory;
 
    }
     public abstract Link createLink(String caption, String url);
 
     public abstract Tray createTray(String caption);
 
     /**
      * createLink、createTray. createPage等方法是用于在抽象工厂中生成零件和产品的方
      * 法。这些方法都是抽象方法,具体的实现被交给了Factory类的子类。不过,这里确定了方法的
      * 名字和签名。
      * @param title
      * @param author
      * @return
      */
     public abstract Page createPage(String title, String author);
 }
 

2.6 定义具体的工厂 ListFactory类

 package cn.design.abstractfactory.listfactory;
 
 import cn.design.abstractfactory.factrory.Factory;
 import cn.design.abstractfactory.factrory.Link;
 import cn.design.abstractfactory.factrory.Page;
 import cn.design.abstractfactory.factrory.Tray;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 11:29
  * @Description TODO
  */
 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.7 定义具体的零件 ListLink类

 package cn.design.abstractfactory.listfactory;
 
 import cn.design.abstractfactory.factrory.Link;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 11:31
  * @Description TODO
  */
 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";
    }
 }
 

2.8 定义具体的零件 ListTray类

 package cn.design.abstractfactory.listfactory;
 
 import cn.design.abstractfactory.factrory.Item;
 import cn.design.abstractfactory.factrory.Tray;
 
 import java.util.Iterator;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-17 11:32
  * @Description TODO
  */
 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 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();
 
    }
 }
 

2.9 定义具体的产品 ListPage类

package cn.design.abstractfactory.listfactory;

import cn.design.abstractfactory.factrory.Item;
import cn.design.abstractfactory.factrory.Page;

import java.util.Iterator;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 11:34
 * @Description TODO
 */
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>\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("<hr><address>" + author + "</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();

    }
}

2.10 测试Main类

package cn.design.abstractfactory;

import cn.design.abstractfactory.factrory.Factory;
import cn.design.abstractfactory.factrory.Link;
import cn.design.abstractfactory.factrory.Page;
import cn.design.abstractfactory.factrory.Tray;
import cn.design.abstractfactory.listfactory.ListFactory;
import cn.design.abstractfactory.tablefactory.TableFactory;
import com.alibaba.fastjson.JSON;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 9:10
 * @Description TODO
 */
public class Main {
    public static void main(String[] args) {
        Factory factory = Factory.getFactory(ListFactory.class.getName());
//        Factory factory = Factory.getFactory(TableFactory.class.getName());

        Link people = factory.createLink("人民日报", "http://www.people.com.cn/");
        Link gmw = factory.createLink(" 光明日报", "http://www.gmw.cn/");
        Link usYahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
        Link jpYahoo = factory.createLink("Yahoo!Japan", "http://ww.yahoo.co.jp/");
        Link excite = factory.createLink("Excite", "http://www.excite.com/");
        Link google = factory.createLink("Google", "http://www.google.com/");

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

        Tray trayYaHoo = factory.createTray("Yahoo!");
        trayYaHoo.add(usYahoo);
        trayYaHoo.add(jpYahoo);

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

        Page page = factory.createPage("LinkPage", " 发哥讲");
        page.add(trayNews);
        page.add(traySearch);
        page.output();
    }
}

运行效果如开篇效果图

3、改造成table工厂示例

3.1 定义TableFactory类

package cn.design.abstractfactory.tablefactory;

import cn.design.abstractfactory.factrory.Factory;
import cn.design.abstractfactory.factrory.Link;
import cn.design.abstractfactory.factrory.Page;
import cn.design.abstractfactory.factrory.Tray;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 14:53
 * @Description TODO
 */
public class TableFactory extends Factory {
    @Override
    public Link createLink(String caption, String url) {
        return new TableLink(caption, url);
    }

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

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

3.2定义TableLink类

package cn.design.abstractfactory.tablefactory;

import cn.design.abstractfactory.factrory.Link;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 14:54
 * @Description TODO
 */
public class TableLink extends Link {
    public TableLink(String caption, String url) {
        super(caption, url);
    }

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

    }
}

3.3定义TableTray类

package cn.design.abstractfactory.tablefactory;

import cn.design.abstractfactory.factrory.Item;
import cn.design.abstractfactory.factrory.Tray;

import java.util.Iterator;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 14:56
 * @Description TODO
 */
public class TableTray extends Tray {
    public TableTray(String caption) {
        super(caption);
    }

    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<td>");
        buffer.append("<table width=\"100%\" border=\"1\"><tr>");
        buffer.append("<td bgcolor=\"#ccccc\" align=\"center\" colspan=\"" + tray.size() + "\"><b>" + caption + "</b></td>");
        buffer.append("</tr>\n");
        buffer.append("<tr>\n");
        Iterator it = tray.iterator();
        while (it.hasNext()) {
            Item item = (Item) it.next();
            buffer.append(item.makeHTML());
        }
        buffer.append("</tr></table>");
        buffer.append("</td>");
        return buffer.toString();

    }
}

3.4定义TablePage类

package cn.design.abstractfactory.tablefactory;

import cn.design.abstractfactory.factrory.Item;
import cn.design.abstractfactory.factrory.Page;

import java.util.Iterator;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 14:57
 * @Description TODO
 */
public class TablePage extends Page {
    public TablePage(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>\n");
        buffer.append("<table width=\"80号\" border=\"3\">\n");
        Iterator it = content.iterator();
        while (it.hasNext()) {
            Item item = (Item) it.next();
            buffer.append("<tr>" + item.makeHTML() + "</tr>");
        }
        buffer.append("</table>\n");
        buffer.append("<hr><address>" + author + "</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();

    }
}

3.5 测试类

测试类与2.10一样。

使用这个即可

Factory factory = Factory.getFactory(TableFactory.class.getName());

3.6效果

image-20200717164531126

4、分析各个模块的作用

4.1 抽象工厂类图

image-20200717164648046

4.2作用介绍

4.2.1 AbstractProduct (抽象产品)

AbstractProduct角色负责定义AbstractFactory角色所生成的抽象零件和产品的接口( API)。在 示例程序中,由Link类、Tray类和Page类扮演此角色。

4.2.2 AbstractFactory (抽象工厂)

AbstractFactory角色负责定义用于生成抽象产品的接口(API)。在示例程序中,由Factory 类扮演此角色。

4.2.3 Client (委托者)

Client角色仅会调用AbstractFactory角色和AbstractProduct角色的接口( API)来进行工作,对 于具体的零件、产品和工厂- -无所知。在示例程序中,由Main类扮演此角色。图8-9省略了 Client这- -角色。

4.2.4 ConcreteProduct (具体产品)

ConcreteProduct角色负责实现AbstractProduct角色的接口( API)。在示例程序中,由以下包中 的以下类扮演此角色。

●listfactory包: ListLink类、ListTray类和ListPage类 ●tablefactory包: TableLink类、TableTray 类和TablePage类

4.2.5 ConcreteFactory (具体工厂)

ConcreteFactory角色负责实现AbstractFactory角色的接口( API)。在示例程序中,由以下包中 的以下类扮演此角色。

●listfactory包: Listfactory类 ●tablefactory 包: Tablefactory类

5、总结

无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模 式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍 加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而 对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。 所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。

5.1 抽象工厂模式的优点

抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产 品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描 述,而不必专门引入一个新的类来进行管理。

5.2 抽象工厂模式的缺点

产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行 修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。

5.3 适用场景

当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。说的更明白一点,就是 一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存

在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则

使用多个独立的工厂来对产品进行创建,则更合适一点。

5.4 对比

抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别 就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程 中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一 个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。

在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成 的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结 构。我们依然拿生产汽车的例子来说明他们之间的区别。

自己尝试写着代码区体会。那种方式方便就是用那种,也是根据场景的改变而做出选择!!

6、延伸( 生成实例的方法)

6.1 new

new 的方式 常用 , 不用多说。

创建一个日期类, 实例:

new Date();

6.2 clone

package cn.design.abstractfactory.clone;

import java.io.Serializable;

/**
 * @author lin
 * @version 1.0
 * @date 2020-07-17 17:02
 * @Description TODO
 */
public class User implements Serializable, Cloneable {
    private static final long serialVersionUID = 6695447736493L;
    String name;
    int age;
    Object obj;

    public User(String name, int age, Object obj) {
        this.name = name;
        this.age = age;
        this.obj = obj;
    }

    public User() {
    }

    public User createClone() {
        User user = null;
        try {
            user = (User) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return user;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", obj=" + obj +
                '}';
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User("发哥讲", 23, "人逢知己千杯少,难得在漫漫人生路上能认识你");
        System.out.println(user.toString());
        User clone = user.createClone();
        System.out.println("clone: " +clone.toString());
    }
}

clone 包含 浅拷贝和深拷贝, 后续有时间 会出一个专辑,专门介绍

6.3 反射

        Class<?> aClass = Class.forName(User.class.getName());
        Object o = aClass.newInstance();
        System.out.println(o);

反射也是后续会出一个专辑, 通过反射 可以获取到类的所有信息,还有类名,属性,方法上的注解.

自定义注解也是一个高级进阶专题的,放心各位,都不会少!!!

 

公众号发哥讲

这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。

代码分享

https://gitee.com/naimaohome

微信公众号 点击关于我,加入QQ群,即可获取到代码以及高级进阶视频和电子书!!

img

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注我们

据说看到好文章不推荐的人,服务器容易宕机!

本文版权归 发哥讲博客园 共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。

 

 

posted @ 2020-07-21 10:05  发哥讲Java  阅读(268)  评论(0编辑  收藏  举报