转载请标明出处:http://blog.csdn.net/shensky711/article/details/53348412

本文出自: 【HansChen的博客】

设计模式系列文章:

设计模式之工厂模式

设计模式之模板方法模式和策略模式

概述

根据依赖倒置原则,我们知道,我们应优先依赖抽象类而不是具体类。在应用开发过程中,有很多实体类都是非常易变的,依赖它们会带来问题,所以我们更应该依赖于抽象接口,以使我们免受大多数变化的影响。

工厂模式(Factory)允许我们只依赖于抽象接口就能创建出具体对象的实例,所以在开发中,如果具体类是高度易变的,那么该模式就非常有用。

接下来我们就通过代码举例说明什么是工厂模式

简单工厂模式

假设我们现在有个需求:把一段数据用Wi-Fi或者蓝牙发送出去。

需求很简单是吧?刷刷刷就写下了以下实现:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
byte[] data = {0x00, 0x01}; if ("Wi-Fi".equals(mode)) {
sendDataByWiFi(data);
} else {
sendDataByBluetooth(data);
}
} private void sendDataByWiFi(byte[] data) {
// send data by Wi-Fi
} private void sendDataByBluetooth(byte[] data) {
// send data by Bluetooth
}

但是上面的代码扩展性并不高,违反了开放封闭原则。比如现在又有了个新的需求,需要用zigbee把数据发送出去,就得再新增一个sendDataByZigbee方法了,而且还得修改onClick里面的逻辑。那么比较好的方法是怎么样的呢?

定义一个数据发送器类:

/**
* 数据发送器Sender
*
* @author HansChen
*/
public interface Sender { void sendData(byte[] data);
}

实现WiFi数据发送:

/**
* Sender的实现类,通过Wi-Fi发送数据
*
* @author HansChen
*/
public class WiFiSender implements Sender { @Override
public void sendData(byte[] data) {
System.out.println("Send data by Wi-Fi");
}
}

实现蓝牙数据发送:

/**
* Sender的实现类,通过蓝牙发送数据
*
* @author HansChen
*/
public class BluetoothSender implements Sender { @Override
public void sendData(byte[] data) {
System.out.println("Send data by Bluetooth");
}
}

这样,原来发送数据的地方就改为了:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
byte[] data = {0x00, 0x01}; Sender sender;
if ("Wi-Fi".equals(mode)) {
sender = new WiFiSender();
} else {
sender = new BluetoothSender();
}
sender.sendData(data);
}

有没有觉得代码优雅了一点?但是随着发送器Sender的实现类越来越多,每增加一个实现类,就需要在onClick里面实例化相应的实现类,能不能用一个单独的类来做这个创造实例的过程呢?这就是我们讲到的工厂。我们新增一个工厂类:

/**
* 简单工厂类
*
* @author HansChen
*/
public class SimpleFactory { public static Sender createSender(String mode) {
switch (mode) {
case "Wi-Fi":
return new WiFiSender();
case "Bluetooth":
return new BluetoothSender();
default:
throw new IllegalArgumentException("illegal type: " + mode);
}
}
}

这样一来,怎么实例化数据发送器我们也不用管了,最终代码变为:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
byte[] data = {0x00, 0x01}; Sender sender = SimpleFactory.createSender(mode);
sender.sendData(data);
}

好了,到这里我们就完成了简单工厂模式的应用了,下图就是简单工厂模式的结构图:

工厂方法模式

简单工厂模式的优点在于工厂类包含了必要的判断逻辑,根据传入的参数动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是这里还是会有个问题,假设上面例子中新增了一个zigbee发送器,那么一定是需要修改简单工厂类的,也就是说,我们不但对扩展开放了,对修改也开放了,这是不好的。解决的方法是使用工厂方法模式,工厂方法模式是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。下面还是通过代码来说明:

在简单工厂模式的基础上,让我们对工厂类也升级一下,首先定义一个工厂类接口:

public interface SenderFactory {

    Sender createSender();
}

然后为每一个发送器的实现类各创建一个具体的工厂方法去实现这个接口

定义WiFiSender的工厂类:

public class WiFiSenderFactory implements SenderFactory {

    @Override
public Sender createSender() {
return new WiFiSender();
}
}

定义BluetoothSender的工厂类:

public class BluetoothSenderFactory implements SenderFactory {

    @Override
public Sender createSender() {
return new BluetoothSender();
}
}

这样,即使有新的Sender实现类加进来,我们只需要新增相应的工厂类就行了,不需要修改原有的工厂,下图就是工厂方法模式的结构图:

客户端调用代码:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
byte[] data = {0x00, 0x01}; SenderFactory factory;
if ("Wi-Fi".equals(mode)) {
factory = new WiFiSenderFactory();
} else {
factory = new BluetoothSenderFactory();
}
Sender sender = factory.createSender();
sender.sendData(data);
}

细心的读者可能已经发现了,工厂方法模式实现时,客户端需要决定实例化哪一个工厂类,相比于简单工厂模式,客户端多了一个选择判断的问题,也就是说,工厂方法模式把简单工厂模式的内部逻辑判断移到了客户端!你想要加功能,本来是修改简单工厂类的,现在改为修改客户端。但是这样带来的好处是整个工厂和产品体系都没有“修改”的变化,只有“扩展”的变化,完全符合了开放封闭原则。

总结

简单工厂模式和工厂方法模式都封装了对象的创建,它们使得高层策略模块在创建类的实例时无需依赖于这些类的具体实现。但是两种工厂模式之间又有差异:

  • 简单工厂模式:最大的优点在于工厂类包含了必要的判断逻辑,根据客户端的条件动态地实例化相关的类。但这也是它的缺点,当扩展功能的时候,需要修改工厂方法,违反了开放封闭原则
  • 工厂方法模式:符合开放封闭原则,但这带来的代价是扩展的时候要增加相应的工厂类,增加了开发量,而且需要修改客户端代码

设计模式之工厂模式(Factory)的更多相关文章

  1. 设计模式之工厂模式(Factory)

    设计模式的工厂模式一共有三种:简单工厂模式,工厂模式,抽象工厂模式 简单工厂模式原理:只有一个工厂类,通过传参的形式确定所创建的产品对象种类 代码如下: #include <stdio.h> ...

  2. python 设计模式之工厂模式 Factory Pattern (简单工厂模式,工厂方法模式,抽象工厂模式)

    十一回了趟老家,十一前工作一大堆忙成了狗,十一回来后又积累了一大堆又 忙成了狗,今天刚好抽了一点空开始写工厂方法模式 我看了<Head First 设计模式>P109--P133 这25页 ...

  3. 设计模式之工厂模式(Factory模式)

    在面向对象系统设计中经常遇到以下两类问题: 1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共接口以形成抽象基类或者接口.这样我们可以通过声明一个指向基类的 ...

  4. 设计模式~简单工厂模式(Factory)

    简单工厂模式Simple Factory根据提供给它的数据,返回一个类的实例.通常它返回的类都有一个公共的父类(或者接口对象). 简单工厂的作用是实例化对象,而不需要客户了解这个对象属于哪个具体的子类 ...

  5. 【设计模式】工厂模式 Factory Pattern

    1)简单工厂(不是模式) 简单工厂只是一种变成习惯,并非23种设计模式之一. 简单工厂提供将实例话那种类型留给运行时判断,而非编译时指定.简单工厂模式就是由一个工厂类根据传入的参数决定创建出哪一个类的 ...

  6. 设计模式之工厂模式 Factory实现

    simpleFactory //car接口 public interface Car { void run(); } //两个实现类 public class Audi implements Car{ ...

  7. JAVA设计模式之工厂模式—Factory Pattern

    1.工厂模式简介 工厂模式用于对象的创建,使得客户从具体的产品对象中被解耦. 2.工厂模式分类 这里以制造coffee的例子开始工厂模式设计之旅. 我们知道coffee只是一种泛举,在点购咖啡时需要指 ...

  8. java设计模式之 工厂模式Factory

    好比某种套路,经过不断实践,证明对项目结构非常有利 如果需要获取某种对象,如同获取不同的产品 需要由工厂来提供,工厂模式 可能的类或者对象:工厂类  产品:Cat  Dog  Fish ... //动 ...

  9. 设计模式(一)工厂模式Factory(创建型)

    设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.可是在一些情况下, new操作符直接生成对象会带来一些问题. ...

  10. 设计模式(一)工厂模式Factory(创建类型)

    设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.可是在一些情况下, new操作符直接生成对象会带来一些问题. ...

随机推荐

  1. unity message

    再用unity进行开发过程中,不可避免的用到消息的传递问题,以下介绍几种消息传递的方法: (一)拖动赋值 此方法即为最普通的方法,即把需要引用的游戏物体或者需要引用的组件拖动到相关公有变量的槽上,然后 ...

  2. fenby C语言 P13

    开关语句switch(变量) switch(weekday) ↓ 数字 default:异常处理 case-break #include <stdio.h> int main() { in ...

  3. springcloud之Feign、ribbon设置超时时间和重试机制的总结

    一 超时时间配置 如果在一个微服务当中对同一个接口同时配置了Hystrix与ribbon两个超时时间,则在接口调用的时候,两个计时器会同时读秒. 比如,访问一个接口需要2秒,你的ribbon配置的超时 ...

  4. spring cloud 调用接口间歇性返回http 500 - Internal Server Error的错误

    查找了各种资料都没找到解决办法,token失效,网络问题,接口服务问题,基础服务问题,都考虑过,但是没能解决,偶尔发现服务器网络配置中存在一个virbr0虚拟网卡,问了下了网管,删除这个对服务并没有影 ...

  5. 二:Mysql库相关操作

    1:系统数据库 information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息.列信息.权限信息.字符信息等.performance_schema: My ...

  6. Unity2-投影方式

  7. 学习笔记63_python反射

    ####反射预备知识一########### __call__ 对象后面加括号,触发执行. python中,类的默认的内置方法,有一个名为__call__,如 class foo: def  __in ...

  8. MongoDB 谨防索引seek的效率问题

    目录 背景 初步分析 索引seeks的原因 优化思路 小结 声明:本文同步发表于 MongoDB 中文社区,传送门: http://www.mongoing.com/archives/27310 背景 ...

  9. 『题解』Codeforces1142B Lynyrd Skynyrd

    更好的阅读体验 Portal Portal1: Codeforces Portal2: Luogu Description Recently Lynyrd and Skynyrd went to a ...

  10. Resource Path Location Type Target runtime Apache Tomcat v6.0 is not defined(项目报错)已解决

    我换了开发工具后,导入的项目不是这里报错就是那里不错.不过,我喜欢.在tomcat里面部署项目后,定位到报错行时,总是提示我这句话:Description Resource Path Location ...