Java设计模式之简单工厂、工厂方法和抽象工厂
在前面的学习中(参见前面的博客),我们学到了很多OO原则:
- 封装变化
- 多用组合,少用继承
- 针对接口/超类编程,不针对实现编程
- 松耦合
- 开闭原则
让我们从一个简单的类开始,看看如何将之改造成符合OO原则的类以及工厂模式在解耦中的威力。
class FoodStore {
public Food orderFood() //通过此方法顾客从食物商店中得到食物
{
Food food=new Food();
food.prepare(); // 准备食物
food.cut(); // 将食物切好
food.box(); // 包装好
return food;
}
}
这样写还不够,食物店里又不是只有一种food,我们要让食物店提供更多!
class FoodStore {
public Food orderFood(String type) {
Food food = null;
if (type.equals("bread")) {
food = new bread();
}
else if(type.endsWith("chicken"))
{
food=new chicken();
}
food.prepare();
food.cut();
food.box();
return food;
}
}
这样我们让食物店提供了面包和鸡肉,他们分别是Food的子类,那我们要在添加一种食物(pizza)呢?这就又需要修改FoodStore的代码!这可不符合开闭原则!但是,从上面两个不同版本的类来看,可以分析出类中变化的部分:食物类型!
既然我们已经找到了变化的部分,根据封装变化的原则,就把这部分(对象的创建)独立开来。
class SimpleFactory {
public Food creatFood(String type)
{
Food food = null;
if (type.equals("bread")) {
food = new bread();
}
else if(type.endsWith("chicken"))
{
food=new chicken();
}
return food;
}
}
这样,我们就完成了今天第一个“模式”——简单工厂,在这个过程中,其实我们只做了一件事:将foodstore中创建对象的部分与其隔离开(其实工厂模式的目的就是封装对象的创建),即:封装变化
下面是封装之后的FoodStore:
class FoodStore {
SimpleFactory factory;
public FoodStore(SimpleFactory factory)
{
this.factory=factory;
}
public Food orderFood(String type) {
Food food = factory.creatFood(type);
food.prepare();
food.cut();
food.box();
return food;
}
}
由于创建对象的过程交给了工厂,所以如果再添加食物类别,只需要修改SimpleFactory类,这样FoodStore就符合了对修改关闭原则!
简单工厂已经很厉害了!但是还不足以应付所有的情况,让我们看看工厂方法和抽象工厂的表现。
问题1:假如有很多商店都向简单工厂去取对象,那么所有商店取得的面包对象和鸡肉对象都是一样的!我们想让不同商店里取得不同口味的食物!
问题2:假如一个商店想要出售两种风味的鸡肉,简单工厂并不能很好的实现。
对于这两个问题,虽然可以在简单工厂中加入多种xxxbread和xxxchicken 来解决,但是会产生很多问题!
分析: 对于问题一,假设第n个商店分别需要n_bread和n_chicken,那么你需要在简单工厂里加入2*n个条件语句,但是,这2*n个语句里,其实只有2个是对其对应的商店有效的,其他的都是累赘!
对于问题二, 假如有n种食物,m种口味,那就是m*n个条件语句,对某个商店来说,有用的其实只有m1*n1个,仍然是有很多冗余代码!
细心的读者会发现,问题一可以看做问题二的子问题,并且都指向了代码的复用性!虽然简单工厂可以复用,但效率太低了!
针对问题一,我们可以将那个具有2*n个条件语句的简单工厂分成n个工厂,与商店一一对应,这样解决了效率问题(不同的商店调用不同的工厂),但是,每个工厂类其实只为对应的商店服务,也就是说,每个工厂类都不需要复用却被我们独立开来!在前文提到过,工厂模式的目的是将对象的创建进行封装,那么能不能将这种封装放在“特殊”的地方呢?当然能!工厂方法模式就是将其放到子类上!
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
方法就是将对象创建部分声明为抽象方法,交给子类去实现,如下:
abstract class Store {
public Food orderFood(String type) {
Food food = creatFood(type);
food.prepare();
food.cut();
food.box();
return food;
}
public abstract Food creatFood(String type); //抽象方法,父类并不知道具体的对象类型,这将由子类决定
}
工厂方法模式相比简单工厂更具有弹性,可以变更正在创建的产品,但是它也放弃了复用性!那怎么能够既保有弹性又能实现复用呢(这正是问题二所面临的问题)?
不用多说,这个自然就是我们的抽象工厂模式,先看看定义吧!
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
如下:
interface abstract_Factory {
public Food creatBread();
public Food creatChicken();
}
做两个实现类,如河北工厂和河南工厂,分别提供河北风味的面包,鸡肉和河南风味的面包,鸡肉,如下:
河北工厂
——————————————
class Hebei_Factory implements abstract_Factory {
@Override
public Food creatBread() {
// TODO Auto-generated method stub
return new Hebei_bread();
}
@Override
public Food creatChicken() {
// TODO Auto-generated method stub
return new Hebei_chicken();
}
}
河南工厂
——————————————
class Henan_Factory implements abstract_Factory {
@Override
public Food creatBread() {
// TODO Auto-generated method stub
return new Henan_bread();
}
@Override
public Food creatChicken() {
// TODO Auto-generated method stub
return new Henan_chicken();
}
}
相应的商店及运行代码
——————————————
class abstract_Factory_Story {
Food food;
public Food orderFood(String type,abstract_Factory factory) {
if("bread".equals(type)){
food = factory.creatBread();
}
else if("chicken".equals(type))
{
food = factory.creatChicken();
}
food.prepare();
food.cut();
food.box();
return food;
}
public static void main(String[] args)
{
abstract_Factory_Story afs=new abstract_Factory_Story();
afs.orderFood("bread", new Hebei_Factory());
afs.orderFood("chicken", new Henan_Factory());
}
}
运行结果
——————————————
Hebei_bread:prepare............ Hebei_bread:cutting............ Hebei_bread:boxed............ Henan_chicken:prepare............ Henan_chicken:cutting............ Henan_chicken:boxed............
可以看出,运行时我们只要传入不同的工厂,就能得到不同风味的食物,各个工厂子类也能复用,在商店类中并不依赖具体类型(food是所有食物的超类),能够轻易的进行扩展而不用修改代码(type的判断放在工厂中会更好)。
总结
纵览三种设计模式,可以发现简单工厂和工厂方法都可以看做抽象工厂的子模式,抽象工厂本身就是工厂方法组合而成(将对象的创建延迟到子工厂),而相应的每个子工厂,也都可以看做是一个简单工厂,只不过在抽象工厂里,运用了面对接口/超类编程的方法将商店类与子工厂具体类型解耦,使之更具有弹性。在很多情况下,简单工厂和工厂方法都能很好的替代抽象工厂,例如,在不需要复用的对象创建封装时,工厂方法比抽象工厂更合适,在创建对象的类型相对确定时(不需要弹性),用简单工厂也能胜任。
Java设计模式之简单工厂、工厂方法和抽象工厂的更多相关文章
- Java设计模式---工厂模式(简单工厂、工厂方法、抽象工厂)
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...
- 设计模式3---工厂模式(Factory Pattern简单工厂、工厂方法、抽象工厂)
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...
- 设计模式(Python)-简单工厂,工厂方法和抽象工厂模式
本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题: 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的 ...
- Java设计模式—工厂方法模式&抽象工厂模式
工厂方法模式与抽象工厂模式都是设计模式中重要而且常见的模式. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 通用类图如下: 在 ...
- c# 设计模式 之:简单工厂、工厂方法、抽象工厂之小结、区别
很多时候,我发现这三种设计模式难以区分,常常会张冠李戴闹了笑话.很有必要深入总结一下三种设计模式的特点.相同之处和不同之处. 1 本质 三个设计模式名字中都含有“工厂”二字,其含义是使用工厂(一个或一 ...
- Java设计模式(2)——创建型模式之工厂方法模式(Factory Method)
一.概述 上一节[简单工厂模式]介绍了通过工厂创建对象以及简单的利弊分析:这一节来看看工厂方法模式对类的创建 工厂方法模式: 工厂方法与简单工厂的不同,主要体现在简单工厂的缺点的改进: 工厂类不再负责 ...
- 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂
三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...
- 结合实例分析简单工厂模式&工厂方法模式&抽象工厂模式的区别
之前写过一篇关于工厂模式(Factory Pattern)的随笔,里面分析了简单工厂模式,但对于工厂方法和抽象工厂的分析较为简略.这里重新分析分析三者的区别,工厂模式是java设计模式中比较简单的一个 ...
- java之设计模式工厂三兄弟之抽象工厂模式
[学习难度:★★★★☆,使用频率:★★★★★] 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工 ...
随机推荐
- 机器学习笔记——K-means
K-means是一种聚类算法,其要求用户设定聚类个数k作为输入參数,因此,在执行此算法前,须要预计须要的簇的个数. 如果有n个点,须要聚到k个簇中.K-means算法首先从包括k个中心点的初始集合開始 ...
- android listview综合使用演示样例_结合数据库操作和listitem单击长按等事件处理
本演示样例说明: 1.自己定义listview条目样式,自己定义listview显示列数的多少,灵活与数据库中字段绑定. 2.实现对DB的增删改查,而且操作后listview自己主动刷新. 3.响应用 ...
- VB.NET 数组的定义 动态使用 多维数组
我们都知道在全部程序设计语言中数组都是一个非常重要的概念,数组的作用是同意程序猿用同一个名称来引用多个变量,因此採用数组索引来区分这些变量.非常多情况下利用数组索引来设置一个循环,这样就能够高效地处理 ...
- 重叠I/O之事件通知
在 Winsock 中,重叠 I/O(Overlapped I/O)模型能达到更佳的系统性能,高于select模型.异步选择和事件选择三种.重叠模型的基本设计原理便是让应用程序使 用一个重叠的数据 ...
- JDK动态代理实现原理--转载
之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了. ...
- Webpack 基本环境搭建
1. 第一步安装之前 先npm init 创建 package.json cnpm init; 然后全局安装 cnpm install webpack -g 确保哪里都可以使用 cnpm instal ...
- 【网络流#1】hdu 3549 - 最大流模板题
因为坑了无数次队友 要开始学习网络流了,先从基础的开始,嗯~ 这道题是最大流的模板题,用来测试模板好啦~ Edmonds_Karp模板 with 前向星 时间复杂度o(V*E^2) #include& ...
- ADSL拨号连接
dotras是一个提供远程访问服务的组件,使用它可以方便的 进行宽带拨号连接 由于不同的系统中dotras调用的底层api可能不同,所以使用时要根据不同的系统使用不同的dotras. dotras目前 ...
- apache 2.4 You don't have permission to access / on this server
用的2.4版本,以前版本解决: 马上打开apache的配置文件httpd.conf,逐行检查.在大约快一半的地方有以下这段代码: <Directory /> Options Foll ...
- 非UI线程和UI线程通信
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = ...