设计模式详解(二):工厂方法模式、抽象工厂模式

2. 工厂方法模式html

(1)概念java

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

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

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

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

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

(2)类图、典型代码ui

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

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

相关文章
相关标签/搜索