设计模式——抽象工厂模式

Posted 博客王大锤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式——抽象工厂模式相关的知识,希望对你有一定的参考价值。

Abstract Factory模式,即抽象工厂模式,该模式要求Factory类为抽象的,并且里面的生产出来的产品也是抽象的!

这里需要着重说的一点就是,抽象类和抽象方法的作用——规范了子类的实现,以及当使用时候,直接可以面向抽象父类/接口的方式进行操作,利用多态的面向对象特性。

同时,需要注意的是,所有抽象的类全都是封装到一个包中的,与调用的不同。

这种方式的一个应用就是JDBC的规范,SUN公司在jdk中提供了相应的接口/抽象父类作为规范,而对应的数据库厂商则根据这个接口/抽象父类去写具体实现。这样一来,我们在操作数据库时候,直接使用sun公司提供的抽象父/接口,而不需要关注具体是怎么实现的,因为不管什么样的数据库,我们操作的代码都是一样的,唯一不同的就是导包不同。

在本次是实例中,使用的一个抽象工厂,以及三个抽象产品。最终希望的就是在E盘的根目录,生成对应的html页面。

在factory抽象类的包中,定义了抽象的类

  • Factory 抽象的 工厂类
package site.wangxin520.gof.abstractfactory.factory;

/**
 * 抽象工厂的类,制作Link,Tray,Page
 * 定义了抽象方法,规范了实现该方法的具体工厂类
 * @author wangXgnaw
 *
 */
public abstract class Factory {

    /**
     * 根据实例工厂的全限定名,获取工厂实例,采用反射的方法
     * @param factoryName 实例化工厂的全限定名
     * @return Factory 返回实例化工厂
     */
    public static Factory getFactory(String factoryName) {
        
        Factory factory=null;
        try {
//            factory = (Factory) Class.forName(factoryName).newInstance();
            factory = (Factory) ClassLoader.getSystemClassLoader().loadClass(factoryName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory;
    }
    
    /**
     * 抽象类,规范了创建Link和Tray和Page的方法
     * @param caption
     * @param url
     * @return
     */
    public abstract Link createLink(String caption,String url);
    public abstract Tray createTray(String caption);
    public abstract Page createPage(String title,String author);
}
  • Item 所有产品类的父类
package site.wangxin520.gof.abstractfactory.factory;

/**
 * 方便统一处理Link和Tray的类
 * Item类是,Link和Tray的父类
 * @author wangXgnaw
 *
 */
public abstract class Item {

    //caption字段表示标题
    protected String caption;
    
    /**
     * 构造函数
     * @param caption
     */
    public Item(String caption){
        this.caption=caption;
    }
    
    /**
     *  制作html,返回HTML文件的内容
     *  本方法是抽象方法,需要子类去实现
     * @return String
     */
    public abstract String makeHTML();
}
  • Link抽象的工厂中的产品,用于记录超链接的
package site.wangxin520.gof.abstractfactory.factory;

/**
 * Link类抽象的表示HTML的链接的类
 * @author wangXgnaw
 *
 */
public abstract class Link extends Item{
    
    //url字段保存的是超链接所指向的地址
    protected String url;

    /**
     * 构造函数,由于继承了一个抽象类,且抽象父类只有一个有参构造
     * 所以,构造函数必须提供,并且调用的是父类的有参构造
     * @param caption
     * @param url
     */
    public Link(String caption,String url) {
        super(caption);
        this.url=url;
    }
    
}
  • Tray抽象工厂的产品,用于存放元素的
package site.wangxin520.gof.abstractfactory.factory;

import java.util.ArrayList;

/**
 * Tray类也要是一个抽象类
 * Tray类表示的是一个含有多喝Link类和Tray类的容器
 * Tray有托盘的意思
 * @author wangXgnaw
 *
 */
public abstract class Tray extends Item{

    //用于保存Tray和Link类,作为容器
    protected ArrayList<Item> tray=new ArrayList<>();
    
    /**
     * 构造方法,调用父类的有参构造
     * @param caption
     */
    public Tray(String caption) {
        super(caption);
    }
    
    /**
     * 使用add方法将Link和Tray类集合在一起
     * @param item
     */
    public void add(Item item){
        tray.add(item);
    }
}
  • Page抽象工厂的产品,页面相关
package site.wangxin520.gof.abstractfactory.factory;

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

/**
 * 抽象的表示HTML页面的类
 * @author wangXgnaw
 *
 */
public abstract class Page {

    //姓名和作者
    protected String title;
    protected String author;
    
    //用于保存页面中的元素
    protected ArrayList<Item> content=new ArrayList<>();
    
    /**
     * page的构造,传入标题和作者
     * @param title
     * @param author
     */
    public Page(String title,String author){
        this.title=title;
        this.author=author;
    }
    
    /**
     * 在页面中存入元素
     * @param item
     */
    public void add(Item item){
        content.add(item);
    }
    
    /**
     * 向文件中写入,这里固定是向E盘根目录中写入一个html文件
     */
    public void output(){
        String filename=title+".html";
        
        FileWriter fw=null;
        try {
            fw = new FileWriter(new File("E:/",filename));
            fw.write(this.makeHTML());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println(filename+"编写完成");
    }

    /**
     * 定义一个制作HTML的方法为子类提供规范
     * @return
     */
    public abstract String makeHTML();
}

在其他包中,实现了以上的接口,方便使用

  • ListFactory工厂类的具体实现
package site.wangxin520.gof.abstractfactory.listfactory;

import site.wangxin520.gof.abstractfactory.factory.Factory;
import site.wangxin520.gof.abstractfactory.factory.Link;
import site.wangxin520.gof.abstractfactory.factory.Page;
import site.wangxin520.gof.abstractfactory.factory.Tray;

/**
 * 表示具体工厂的类
 * 制作ListLink,ListTray,ListPage
 * @author wangXgnaw
 *
 */
public class ListFactory extends Factory{

    /**
     * 返回具体的ListLink实例,下同
     */
    @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);
    }

}
  • ListLink上面Link类的具体实现
package site.wangxin520.gof.abstractfactory.listfactory;

import site.wangxin520.gof.abstractfactory.factory.Link;

/**
 * 具体零件,表示HTML的连接的类
 * @author wangXgnaw
 *
 */
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><br/>";
    }

}
  • ListTray Tray类的具体实现
package site.wangxin520.gof.abstractfactory.listfactory;

import site.wangxin520.gof.abstractfactory.factory.Item;
import site.wangxin520.gof.abstractfactory.factory.Tray;

/**
 * 具体零件,表示含有Link和Tray的类
 * 
 * @author wangXgnaw
 *
 */
public class ListTray extends Tray {

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

    @Override
    public String makeHTML() {

        StringBuilder sb = new StringBuilder();
        sb.append("<li>").append(caption).append("<ul>");

        for (Item item : tray) {
            sb.append(item.makeHTML());
        }

        sb.append("</ul>").append("</li>");

        return sb.toString();
    }

}
  • ListPage Page类的具体实现
package site.wangxin520.gof.abstractfactory.listfactory;

import site.wangxin520.gof.abstractfactory.factory.Item;
import site.wangxin520.gof.abstractfactory.factory.Page;

/**
 * 具体零件,表示HTML页面的类
 * @author wangXgnaw
 *
 */
public class ListPage extends Page{

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

    @Override
    public String makeHTML() {
        
        StringBuilder sb=new StringBuilder();
        
        sb.append("<html><head><title>"+title+"</title></head>");
        
        sb.append("<body>");
        sb.append("<h1>"+title+"</h1>");
        sb.append("<ul>");
        for (Item item : content) {
            sb.append(item.makeHTML());
        }
        sb.append("</ul>");
        sb.append("<hr>");
        sb.append("<address>"+author+"</address>");
        
        sb.append("</body></html>");
        
        return sb.toString();
    }

}

测试类以及测试方法

package site.wangxin520.gof.abstractfactory;

import site.wangxin520.gof.abstractfactory.factory.Factory;
import site.wangxin520.gof.abstractfactory.factory.Link;
import site.wangxin520.gof.abstractfactory.factory.Page;
import site.wangxin520.gof.abstractfactory.factory.Tray;

/**
 * 抽象工厂模式的测试类
 * @author wangXgnaw
 *
 */
public class Test {

    public static void main(String[] args){
        
        //获取工厂实体,参数是实体工厂的全限定名
        Factory factory=Factory.getFactory("site.wangxin520.gof.abstractfactory.listfactory.ListFactory");
        
        //创建超链接
        Link link1 = factory.createLink("王鑫", "http://www.wangxin520.site/");
        Link link2 = factory.createLink("淘宝", "https://www.taobao.com/");
        Link link3 = factory.createLink("京东", "https://www.jd.com/");
        
        //创建序列,并且将超链接放到序列中
        Tray t1 = factory.createTray("主页");
        t1.add(link1);
        Tray t2 = factory.createTray("购物");
        t2.add(link2);
        t2.add(link3);
        
        //创建页面,并且将序列放到页面里面
        Page page = factory.createPage("首页", "王鑫");
        page.add(t1);
        page.add(t2);
        
        //调用page的output()方法去实现写入文件
        page.output();
    }
    
}

使用抽象工厂的好处:

在这种模式下,增加具体的工厂是很容易的,就像如果有新的数据库厂商过来想要开发,直接让他们实现具体的规范即可。

不过这种模式也是具有缺陷的:

不方便增加新的产品。

实现效果:

  • 生成文件

image

  • 打开效果

image

以上是关于设计模式——抽象工厂模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式

创建型模式--抽象工厂

对比总结三个工厂模式(简单工厂,工厂方法,抽象工厂)

案例分析:设计模式与代码的结构特性

Yii2设计模式——注册树模式

Java入门——面向对象基础