zoukankan      html  css  js  c++  java
  • 设计模式详解(二):工厂方法模式、抽象工厂模式

    2. 工厂方法模式

    (1)概念

    工厂方法模式的定义是:定义一个用于创建对象的接口,让子类决定实现哪一个类。

    即工厂父类负责定义创建产品对象的公共接口,工厂子类负责生成具体的产品对象。

    将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

    工厂方法模式是简单工厂模式的延伸与改进,既继承了其封装性等优点,又弥补了其缺陷(提高扩展性),使其符合原则的要求。

    在工厂方法模式中,每一个具体工厂只能生产一种具体产品。具体工厂与具体产品一一对应。

    (2)类图、典型代码

    Factory(抽象类或接口):抽象工厂类,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,它与应用程序无关。

    ConcreteFactory(实现类):具体工厂类,实现抽象工厂类中声明的方法,可由客户端调用,返回一个具体产品类的实例。

    Product(抽象类或接口):抽象产品类,定义了产品的接口,是产品对象的共同父类或接口。

    ConcreteProduct(实现类):具体产品类,实现了抽象产品类的方法,其具体产品由对应的具体工厂创建。

    典型代码如下:

    //抽象工厂类
    public interface Factory {
        public Product factoryMethod();
    }
    //具体工厂类之一
    public class ConcreteFactory implements Factory {
        @Override
        public Product factoryMethod() {
            return new ConcreteFactory();
        }
    }

    (3)举例

    原有一个工厂生产一种电视机,现分为两个子工厂:海尔工厂生产海尔电视机,海信工厂生产海信电视机。

    根据工厂方法模式设计类图如下:

    实现代码如下:

    //抽象产品类,定义所有产品必须实现的方法
    public interface TV {
        public void play();
    }
    //具体产品类1--海尔电视
    public class HaierTV implements TV {
    
        @Override
        public void play() {
            // 海尔电视的功能
            System.out.println("海尔电视播放中...");
        }
    
    }
    //具体产品类2--海信电视
    public class HisenseTV implements TV {
    
        @Override
        public void play() {
            // 海信电视的功能
            System.out.println("海信电视播放中...");
        }
    
    }
    //抽象工厂类,定义所有工厂必须实现的方法
    public interface TVFactory {
        public TV produceTV();
    }
    //具体产品类1--海尔电视
    public class HaierTV implements TV {
    
        @Override
        public void play() {
            // 海尔电视的功能
            System.out.println("海尔电视播放中...");
        }
    
    }
    //具体产品类2--海信电视
    public class HisenseTV implements TV {
    
        @Override
        public void play() {
            // 海信电视的功能
            System.out.println("海信电视播放中...");
        }
    
    }
    //客户端调用类--调用具体电视工厂生产对应电视
    public class Client {
    
        public static void main(String[] args) {
            TVFactory factory;
            TV tv;
    
            // 产生海尔电视并调用其功能
            factory = new HaierTVFactory();
            tv = factory.produceTV();
            tv.play();
    
            // 产生海信电视并调用其功能
            factory = new HisenseTVFactory();
            tv = factory.produceTV();
            tv.play();
        }
    
    }

    输出结果如下:

    海尔电视播放中...
    海信电视播放中...

    如果此时还需要增加一种电视机品牌TCL,则只需要新建一个实现TV接口的具体产品类和一个实现TVFactory接口的具体工厂类即可,不需要修改其他代码:

    //新增具体产品类3--TCL电视
    public class TCLTV implements TV {
    
        @Override
        public void play() {
            // TCL电视的功能
            System.out.println("TCL电视播放中...");
        }
    
    }
    //新增具体工厂类3--专门生产TCL电视的TCL工厂
    public class TCLTVFactory implements TVFactory {
    
        @Override
        public TV produceTV() {
            // 生产一个TCL电视的对象
            return new TCLTV();
        }
    
    }

    在客户端调用类中增加调用TCL工厂生产TCL电视的语句:

            // 产生TCL电视并调用其功能
            factory = new TCLTVFactory();
            tv = factory.produceTV();
            tv.play();

    输出结果如下:

    海尔电视播放中...
    海信电视播放中...
    TCL电视播放中...

    (4)优缺点、适用场景

    工厂方法模式的优点:

      用户只需关心所需产品对应的工厂,无须关心创建细节(屏蔽产品类),甚至不需要知道具体产品的类名;

      典型的解耦框架,高层模块需要知道产品的抽象类,其他的实现类都不用关心;

      良好的封装性,代码结构清晰,优秀的扩展性,同时符合开闭原则。

    工厂方法模式的缺点:

      在添加新产品时成对增加了类的个数,增加了系统的复杂度,编译和运行更多的类也会增加系统的开销;

      考虑到可扩展性引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。

    工厂方法模式的适用场景:

      需要灵活、可扩展的框架时;

      当一个类(比如客户端类)不知道所需要的对象的类时(需要知道其对应的工厂);

      一个类通过其子类来确定创建那个对象。

    3. 抽象工厂模式

    (1)概念

    抽象工厂模式的定义是:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

    抽象工厂模式是工厂方法模式的泛化版,即工厂方法模式只是抽象工厂模式的一种特殊情况。

    在抽象工厂模式中,每一个具体工厂可以生产多个具体产品。

    (2)类图、典型代码

     

    AbstractFactory(抽象类或接口):抽象工厂类,用于声明生产产品的方法。

    ConcreteFactory(实现类):具体工厂类,具体实现生产产品的方法,返回一个具体产品。

    AbstractProduct(抽象类或接口):抽象产品类,定义了每种产品的功能方法。

    ConcreteProduct(实现类):具体产品类,具体实现了抽象产品类定义的功能方法。

    典型代码如下:

    // 抽象工厂类
    public interface AbstractFactory {
        public AbstractProductA createProductA();
        public AbstractProductB createProductB();
    }
    // 具体工厂类之一
    public class ConcreteFactory implements AbstractFactory{
        public AbstractProductA createProductA(){
            return new ConcreteProductA();
        }
        public AbstractProductB createProductB(){
            return new ConcreteProductB();
        }
    }

    (3)举例

    一个电器工厂可以生产多种电器,比如海尔工厂可以生产海尔电视和海尔空调,TCL工厂可以生产TCL电视和TCL空调。

    根据抽象工厂模式设计类图如下:

    实现代码如下:

    //抽象产品类1--电视类
    public interface TV {
        public void play();
    }
    //电视具体产品类1--海尔电视
    public class HaierTV implements TV {
    
        @Override
        public void play() {
            System.out.println("海尔电视播放中...");
        }
    
    }
    //电视具体产品类2--TCL电视
    public class TCLTV implements TV {
    
        @Override
        public void play() {
            System.out.println("TCL电视播放中...");
        }
    
    }
    //抽象产品类2--空调类
    public interface AirConditioner {
        public void changeTemperature();
    }
    //空调具体产品类1--海尔空调
    public class HaierAirConditioner implements AirConditioner {
    
        @Override
        public void changeTemperature() {
            System.out.println("海尔空调吹风中...");
        }
    
    }
    //空调具体产品类2--TCL空调
    public class TCLAirConditioner implements AirConditioner {
    
        @Override
        public void changeTemperature() {
            System.out.println("TCL空调吹风中...");
        }
    
    }
    //抽象工厂类,定义所有工厂必须实现的方法
    public interface Factory {
        public TV produceTV();
    
        public AirConditioner produceAirConditioner();
    }
    //具体工厂类1--海尔工厂
    public class HaierFactory implements Factory{
    
        @Override
        public TV produceTV() {
            return new HaierTV();
        }
    
        @Override
        public AirConditioner produceAirConditioner() {
            return new HaierAirConditioner();
        }
    
    }
    //具体工厂类2--TCL工厂
    public class TCLFactory implements Factory{
    
        @Override
        public TV produceTV() {
            return new TCLTV();
        }
    
        @Override
        public AirConditioner produceAirConditioner() {
            return new TCLAirConditioner();
        }
    
    }

    运行结果如下:

    海尔电视播放中...
    海尔空调吹风中...
    ———————————————
    TCL电视播放中...
    TCL空调吹风中...

    (4)优缺点、适用场景

    抽象工厂模式的优点:

      隔离了具体类的生成,使客户并不知道什么被创建;

      产品内的约束为非公开状态(比如不同产品的生产比例,这对调用工厂类的高层模块是透明的);

    抽象工厂模式的缺点:

      在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品(对接口进行扩展会导致所有子类的修改);

    抽象工厂模式的适用场景:

      一个系统不依赖产品类实例如何被创建、组合和表达的细节时;

      系统中有多个产品族,而每次只使用其中某一产品族时;

      属于同一产品族的产品将一起使用;

      多个对象有相同的约束时。

    在实际的应用开发中,一般将具体类的类名写入配置文件中,再通过Java的反射机制读取XML格式的配置文件,根据存储在XML文件中的类名字符串生成对象。

    新建XML文件config.xml如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <config>
        <className>xxxFactory</className>
    </config>

    新建工具类文件XMLUtil.java如下:

    package com.test.util;
    
    import java.io.File;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    
    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    public class XMLUtil {
        // 该方法用于从XML配置文件中提取类名字符串,并返回一个实例对象
        public static Object getBean() {
            try {
                // 创建DOM文档对象
                DocumentBuilderFactory dFactory = DocumentBuilderFactory
                        .newInstance();
                DocumentBuilder builder = dFactory.newDocumentBuilder();
                Document doc = builder.parse(new File(
                        "./src/com/test/util/config.xml"));// 若不在同一路径下,必须指定文件具体路径,用正斜杠/或双反斜杠\
    
                // 获取包含类名的文本节点
                NodeList nlist = doc.getElementsByTagName("className");
                Node classNode = nlist.item(0).getFirstChild();
                String cName = classNode.getNodeValue();
    
                // 通过类名生成实例对象并将其返回
                Class c = Class.forName("com.test.factory_method." + cName);// 若不在同一路径下,必须写出类的全名
                Object obj = c.newInstance();
                return obj;
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    改进工厂方法模式中的客户端代码如下:

    //客户端调用类--调用具体电视工厂生产对应电视
    public class Client {
    
        public static void main(String[] args) {
            TVFactory factory;
            TV tv;
    
    //        // 产生海尔电视并调用其功能
    //        factory = new HaierTVFactory();
    //        tv = factory.produceTV();
    //        tv.play();
    //
    //        // 产生海信电视并调用其功能
    //        factory = new HisenseTVFactory();
    //        tv = factory.produceTV();
    //        tv.play();
    //        
    //        // 产生TCL电视并调用其功能
    //        factory = new TCLTVFactory();
    //        tv = factory.produceTV();
    //        tv.play();
            
            // 将具体类名写入配置文件中,再通过Java反射机制读取XML格式文件,根据类名生成对象并返回
            factory = (TVFactory) XMLUtil.getBean();
            tv = factory.produceTV();
            tv.play();
        }
    
    }

    将config.xml文件中的“xxxFactory”更改为需要生成对象的类名“HaierTVFactory”,运行客户端结果如下:

    海尔电视播放中...

    抽象工厂模式中例子的客户端改进与此相同。

     

    6大设计原则,与常见设计模式(概述):http://www.cnblogs.com/LangZXG/p/6204142.html

    类图基础知识:http://www.cnblogs.com/LangZXG/p/6208716.html

    注:转载请注明出处   http://www.cnblogs.com/LangZXG/p/6249425.html

  • 相关阅读:
    Linux几个常用的目录结构
    Linux 安装composer
    Elasticsearch修改network后启动失败
    php7 闭包调用
    php 爬虫框架
    file_get_contents('php://input') 和POST的区别
    PHP指定日期转时间戳
    .Net 站点跨域问题及解决方法
    C# 多线程学习系列一
    Nginx学习系列四默认负载均衡轮询及Ip_hash等常用指令介绍
  • 原文地址:https://www.cnblogs.com/LangZXG/p/6249425.html
Copyright © 2011-2022 走看看