[置顶] 设计模式(一)工厂模式

官方定义:设计模式(Design Pattern)是一套被反复使用、多数人知晓的、通过分类编目的代码设计经验总结。java

使用意义:使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码的可靠性。可复用的面向对象软件系统主要包含两大类:应用程序工具箱和框架(框架是指构成一类特定软件可复用设计的一组相互协做的类)。设计模式有助于对框架的理解,成熟的框架一般使用了多种设计模式,若是你能熟悉这些设计模式,毫无疑问,将能迅速的掌握框架的结构。数据库

工厂模式概念

工厂模式将客户类与工厂类分开,消费者在任什么时候候须要某种产品只须要向工厂请求,消费者无须修改就能接纳新产品。缺点:产品修改时,工厂类也要作相应的修改。工厂模式是编程过程当中最经常使用的设计模式,由于工厂模式至关于建立实例对象的new, 例如 A a = new A();工厂类也是用来建立实例对象的,那么,new关键字能够建立对象,为何还要用工厂类建立实例对象呢?因为建立实例时一般伴随有初始化工做,若是只是简单的赋值操做,咱们可使用带参数的构造方法,但若是初始化不只仅是赋值这样简单的操做,还包含一些逻辑判断等大片断的代码,若是都写进构造函数,会使咱们的代码看起来很臃肿,将多个功能放进一个方法,就像将不少鸡蛋放进一个篮子里,这很危险,而且有悖于咱们面向对象的设计原则。这时须要咱们将长片断的代码进行分割,再封装,之后若是须要修改,只须要修改部分片断,而不会出现牵一动百的情形。编程

简单工厂模式

简单工厂模式的是想源于java中的接口,经过使用接口能够实现不相关类的相同行为,而不须要考虑这些类的层次关系,接口就是实现类对外的外观。主要使用在业务比较简单的状况下,由三种角色构成。设计模式

  • 工厂类:是模块的核心,一般包含逻辑实现,在java中每每是一个具体的实现类
  • 抽象产品:是具体产品继承的父类或者须要实现的接口,在java中一般是抽象类或者接口
  • 具体产品:工厂类建立的对象就是该类的实例,在java中一般是具体的实现类

简单工厂类的基本结构


Client与具体实现Impl之间并没有瓜葛,客户端经过Factory获取须要的接口对象,而后调用接口的方法来实现具体的功能。下面举个例子,经过不一样的通信工具来通讯,QQ和微信分别实现了API接口。微信

示例说明

/**
 * 简单工厂模式
 * Created by xian.juanjuan on 2017-7-13 14:07.
 */
public class SimpleFactory {
    //根据条件建立具体的实现对象
    public static MessageApi createMessageApi(int condition){
        MessageApi messageApi = null;
        if (condition == 1){
            messageApi = new QQMessageImpl();
        } else if (condition == 2){
            messageApi = new WeChatMessageImpl();
        }
        return messageApi;
    }
}

class Client{
    public static void main(String[] args){
        //经过简单工厂获取接口对象
        MessageApi messageApi = SimpleFactory.createMessageApi(1);
        messageApi.sendMessage("测试简单工厂模式");
    }
}

/**
 * 通讯接口能够经过简单工厂来建立
 */
interface MessageApi{
    //具体的功能方法
    void sendMessage(String msg);
}

/**
 * 接口的具体实现--QQ通讯
 */
class QQMessageImpl implements MessageApi{
    @Override
    public void sendMessage(String msg) {
        System.out.println("经过QQ聊天工具发送消息:"+msg);
    }
}

/**
 * 接口的具体实现--微信通讯
 */
class WeChatMessageImpl implements MessageApi{
    @Override
    public void sendMessage(String msg) {
        System.out.println("经过微信聊天工具发送消息"+msg);
    }
}

问题1:这里若是再增长一种通信方式(Email)时,就须要修改工厂类,显然简单工厂方式不能知足OCP设计原则。

问题2:客户端在调用工厂时,须要传入选择的参数,这就说明客户端必须知道每一个参数的含义,也须要知道每一个参数对应的功能处理,在必定程度上向客户端暴露了内部实现细节。框架

每次增长一个实现类就要就要修改工厂类的实现,一般用配置文件类解决这个问题。less

建立FactoryTest.properties配置文件,内容以下,若是新增实现类,只须要修改下面的配置文件便可。ide

ImplClass=com.xianjj.pattern.QQMessageImpl

工厂类以下函数

public class SimpleFactory {

    public static MessageApi createMessageApi(){
        Properties properties = new Properties();
        InputStream inputStream;
        try {
            inputStream = SimpleFactory.class.getResourceAsStream("FactoryTest.properties");
            properties.load(inputStream);
        } catch (IOException e) {
            System.out.println("加载工厂配置文件异常");
            e.printStackTrace();
        }
        //用反射建立对象
        MessageApi messageApi = null;
        try {
            messageApi = (MessageApi)Class.forName(properties.getProperty("ImplClass")).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return messageApi;
    }
}

简单工厂模式优缺点

  • 帮助封装 很是友好的帮咱们实现了组件的封装,让组件外部能真正的面向接口编程
  • 解耦 实现客户端和具体实现类的解耦
  • 可能增长客户端的复杂度 如何选择具体的实现类须要仔细斟酌
  • 不方便扩展子工厂 私有化简单工厂的构造方法,使用静态方法来建立接口,也就不能经过写简单工厂类的子类来改变建立接口的方法的行为了
  • 不彻底知足OCP
简单工厂的实质是为了“选择实现”,工厂的重点在于选择,由于实现已经作好了,目的是为客户端选择相应的实现,从而使得客户端与实现之间解耦。难点在于如何选择实现,能够经过传递参数,或动态的读取某个内存的值,或者读取数据库中的值来选择具体的实现。

简单工厂一般不用建立简单工厂类的类实例,所以,能够把简单工厂类看作一个工具类,直接使用静态方法就能够了,因此被称做静态工厂。为了防止客户端无谓的建立简单工厂实例,能够把简单工厂的构造方法私有化。工具

工厂方法模式

为了解决简单工厂模式最大的缺点—不彻底知足OCP原则,设计师们提出了工厂方法模式,工厂方法模式相对于简单工厂模式来讲最大的不一样在于,对于一个项目或者一个独立的模块来讲,简单工厂模式只有一个工厂类,儿工厂方法模式有一组实现了相同接口的工厂类。

在工厂方法模式中,核心的工厂类再也不负责产品的建立,而是将具体的建立工做交给子类去作。这个核心工厂则变为抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触产品建立的细节。在工厂方法模式中通常都有一个平行的等级结构,也就是说工厂和产品是对应的。

工厂方法模式结构


示例说明

果农起初只有一个小型的苹果园,后来规模扩大,出现了苹果工厂,葡萄工厂,每一个工厂独立生产对应的水果,实现专业化和规模化的生产。

/**
 * 工厂方法模式示例
 * Created by xian.juanjuan on 2017-7-14 09:30.
 */
public class ClientTest {
    public static void main(String[] args){
        //实例化水果工厂
        FruitFactory fruitFactory = new AppleFactoryImpl();
        FruitFactory fruitFactory1 = new GrapeFactoryImpl();
        //从水果工厂生产水果
        Fruit fruit = fruitFactory.factory();
        Fruit fruit1 = fruitFactory1.factory();
        //不一样水果的生长过程
        fruit.plant();
        fruit1.harvest();
    }
}

/**
 * 水果接口
 */
interface Fruit{
    void plant();//种植
    void harvest();//收获
}

/**
 * 水果工厂的产品:苹果
 */
class Apple implements Fruit{
    private int treeAget;//树龄

    @Override
    public void harvest() {
        System.out.println("苹果已经收获。。。");
    }

    @Override
    public void plant() {
        System.out.println("苹果已经种植。。。");
    }

    public int getTreeAget() {
        return treeAget;
    }

    public void setTreeAget(int treeAget) {
        this.treeAget = treeAget;
    }
}

/**
 * 水果工厂的产品:葡萄
 */
class Grape implements Fruit{
    private boolean seedless;//是否有籽

    @Override
    public void harvest() {
        System.out.println("葡萄已经收获。。。");
    }

    @Override
    public void plant() {
        System.out.println("葡萄已经种植。。。");
    }

    public boolean isSeedless() {
        return seedless;
    }

    public void setSeedless(boolean seedless) {
        this.seedless = seedless;
    }
}

/**
 * 水果工厂接口
 */
interface FruitFactory{
    Fruit factory();
}

/**
 * 苹果工厂实现
 */
class AppleFactoryImpl implements FruitFactory{
    @Override
    public Fruit factory() {
        Fruit fruit = new Apple();
        System.out.println("水工工厂成功建立一个水果:苹果");
        return fruit;
    }
}

/**
 * 葡萄工厂实现
 */
class GrapeFactoryImpl implements FruitFactory{
    @Override
    public Fruit factory() {
        Fruit fruit = new Grape();
        System.out.println("水工工厂成功建立一个水果:葡萄");
        return fruit;
    }
}

输出结果

水工工厂成功建立一个水果:苹果
水工工厂成功建立一个水果:葡萄
苹果正在生长。。。
葡萄已经收获。。。

Process finished with exit code 0

后续工厂继续扩建,若是须要草莓工厂,只须要新增对应的草莓实现类和草莓工厂实现类便可,对原有实现无任何影响。

工厂方法模式优缺点(相对于简单工厂模式)

  • 结构复杂度增长
  • 代码复杂度增长
  • 客户端编程难度增长(客户端编程中须要对须要对工厂类进行实例化,而简单工厂时静态类不须要实例化)
  • 管理难度增大
  • 知足OCP原则
从维护的角度来讲,假如每一个产品类须要进行必定的修改,极可能须要修改对应的工厂类,当同时须要修改多个产品类时,须要找对应的工厂类进行修改,这种模式就变得至关麻烦。综上,简单工厂模式更简单,更方便一些,而工厂方法模式更先进一些。须要根据实际状况选择合适的设计模式。

抽象工厂模式

抽象工厂模式是三个最抽象,最具通常性的。抽象工厂模式的目的是给客户端提供一个接口,能够建立多个产品族中的产品对象,使用抽象工厂模式须要知足如下条件

  • 系统中有多个产品族,而系统一次只可能消费其中一族产品
  • 同属于一个产品族的产品一块儿使用

举例说明两个概念

  • 产品族:Android产品族和Apple产品族
  • 产品等级(产品族的产品):手机和充电器。(Android手机和Android充电器是一个产品族,Apple手机和Apple充电器是另外一个产品族;Android手机用Android充电器,苹果手机用苹果充电器)一个等级结构是由结构相同的产品组成。

抽象工厂模式的每一个工厂创造出的都是一族的产品,而不是一个或者一组。

产品结构图


示例说明

用户买手机配充电器

首先用简单工厂模式实现

public class AbstractFactoryTest {

    public static void main(String[] args){
        phoneCharging(1, 1);
    }

    public static void phoneCharging(int phoneType, int chargerType){
        //手机工厂和充电器工厂生产手机和充电器
        Phone phone = PhoneFactory.createPhone(phoneType);
        Charger charger = ChargerFactory.createCharger(chargerType);
        //买手机配充电器
        phone.buyPhone();
        charger.configureCharger();
    }
}

/**
 * 手机接口
 */
interface Phone{
    void buyPhone();//买手机
}

class AndroidPhone implements Phone{
    @Override
    public void buyPhone() {
        System.out.println("我买了一部Android手机");
    }
}

class ApplePhone implements Phone{
    @Override
    public void buyPhone() {
        System.out.println("我买了一部Apple手机");
    }
}

/**
 * 充电器接口
 */
interface Charger{
    void configureCharger();//配置充电器
}

class AndroidCharger implements Charger{
    @Override
    public void configureCharger() {
        System.out.println("给我配置了Android手机充电器");
    }
}

class AppleCharger implements Charger{
    @Override
    public void configureCharger() {
        System.out.println("给我配置了Apple手机充电器");
    }
}

/**
 * 手机工厂类
 */
class PhoneFactory{

    public static Phone createPhone(int type){
        Phone phone = null;
        if (1 == type){
            phone =  new AndroidPhone();
        } else if (2 == type){
            phone =  new ApplePhone();
        }
        return phone;
    }
}

/**
 * 充电器工厂类
 */
class ChargerFactory{
    public static Charger createCharger(int type){
        Charger charger = null;
        if (1 == type){
            charger = new AndroidCharger();
        } else if(2 == type){
            charger = new AppleCharger();
        }
        return charger;
    }
}

运行结果

我买了一部Android手机
给我配置了Android手机充电器

Process finished with exit code 0

外部使用的时候,手机与充电器的类型须要匹配,若是phoneCharging(1, 2);那手机和充电器就不兼容;在上述的简单工厂模式中,并无维护这种关系,为了解决这个问题 须要使用抽象工厂模式。简单工厂模式针对的是一个产品等级结构,而抽象工厂模式则须要面对多个产品等级结构。


用同一个工厂等级结构复杂两个不一样产品等级结构中的产品对象的建立,即Apple工厂生产Apple手机和Apple手机充电器。因为这两个产品族的产品等级结构相同,所以使用同一个工厂族也能够处理这两个产品族的建立问题,这就是抽象工厂模式。


使用抽象工厂模式实现

在以前代码的基础上,只须要增长抽象工厂类和实现便可。

interface AbstractFactory{
    Phone createPhone();
    Charger createCharger();
}

class AndroidFactory implements AbstractFactory{
    @Override
    public Charger createCharger() {
        return new AndroidCharger();
    }

    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }
}

class AppleFactory implements AbstractFactory{
    @Override
    public Charger createCharger() {
        return new AppleCharger();
    }

    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }
}
测试客户端
public class AbstractFactoryTest {

    public static void main(String[] args){
        //客户选择并建立须要的产品对象
        AbstractFactory factory = new AppleFactory();
        //让店员配套起来便可
        phoneCharging(factory);
    }

    public static void phoneCharging(AbstractFactory factory){
        //店员找相应的工厂获取产品
        Phone phone = factory.createPhone();
        Charger charger = factory.createCharger();
        phone.buyPhone();
        charger.configureCharger();
    }
}

因而可知,抽象工厂模式是为一系列相关的对象或者相互依赖的对象建立一个接口。

抽象工厂模式优缺点

  • 分离接口和实现(客户端从具体的产品实现中解耦)
  • 使切换产品族变的容易(从Android切换到Apple,客户端只须要切换一下具体工厂)
  • 不容易扩展新产品(若是给整个产品族新增一个产品,就要修改抽象工厂,致使修改全部的工厂实现类)
工厂模式在此就叙述完了,实际应用中可根据具体需求选择使用。
相关文章
相关标签/搜索