在面向对象编程中,最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下,new操作符直接生成对象会带来一些问题。举例来说,许多类型对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置;选择生成哪个子对象实例;或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。

动机

工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

分类

工厂模式可以分为三类:

1)简单工厂模式(Simple Factory)

2)工厂方法模式(Factory Method)

3)抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。

区别

1)工厂方法模式:

  • 一个抽象产品类,可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类只能创建一个具体产品类的实例。

2)抽象工厂模式:

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类可以创建多个具体产品类的实例。

区别:

  • 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
  • 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

下面具体介绍下这三种工厂模式。

一、 简单工厂模式

简介

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

延伸

试想一下,当我们在codeing的时候,在A类里面只要NEW了一个B类的对象,那么A类就会从某种程度上依赖B类。如果在后期需求发生变化或者是维护的时候,需要修改B类的时候,我们就需要打开源代码修改所有与这个类有关的类了,做过重构的朋友都知道,这样的事情虽然无法完全避免,但确实是一件让人心碎的事情。

组成

  1. 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。
  2. 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。
  3. 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

优点

简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

缺点

很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则。

模拟场景

联想电脑旗下有很多产品,其中有一类新产品叫ThinkPad,该产品有几个版本,其中两个叫ThinkPadS1和ThinkPadS2,用户不用去创建ThinkPad。因为客户有一个工厂来帮他创建ThinkPad,想要什么版本,这个工厂就可以建。比如想要S1版本,工厂就创建这个版本的ThinkPad。即工厂可以创建产品。

类图

图1 简单工厂模式实例类图

实例

Java

//产品类
abstract class ThinkPad{
public ThinkPad() {
//Console.WriteLine("This is ThinkPad!");
}
}
class ThinkPadS1 extends ThinkPad{
public ThinkPadS1() {
System.out.println("This is ThinkPadS1!");
}
}
class ThinkPadS2 extends ThinkPad{
public ThinkPadS2() {
System.out.println("This is ThinkPadS2!");
}
}
//工厂类
package FactoryDemo;
public class SimpleFactory {
public ThinkPad GetTypeofThinkPad(int type){
switch (type){
case 1: return new ThinkPadS1();
case 2: return new ThinkPadS2();
default: break;
}
return null;
}
}
package FactoryDemo;
//客户类
public class Client {
public static void main(String[] args) {
// 简单工厂模式
SimpleFactory factory = new SimpleFactory();
ThinkPad tps1=factory.GetTypeofThinkPad(1);
ThinkPad tps2 = factory.GetTypeofThinkPad(2);
}
}

C#

    //产品类
public abstract class ThinkPad
{
public ThinkPad()
{
//Console.WriteLine("This is ThinkPad!");
}
}
public class ThinkPadS1 : ThinkPad
{
public ThinkPadS1()
{
Console.WriteLine("This is ThinkPadS1!");
}
}
public class ThinkPadS2 : ThinkPad
{
public ThinkPadS2()
{
Console.WriteLine("This is ThinkPadS2!");
}
}
//工厂类
public class SimpleFactory
{
public ThinkPad GetTypeofThinkPad(string type)
{
switch (type)
{
case "S1": return new ThinkPadS1();
case "S2": return new ThinkPadS2();
default: break;
}
return null;
}
}
//客户类
class Program
{
static void Main(string[] args)
{
// /*简单工厂模式
SimpleFactory factory = new SimpleFactory();
ThinkPad tps1=factory.GetTypeofThinkPad("S1");
ThinkPad tps2 = factory.GetTypeofThinkPad("S2");
}
}

下面我们从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当客户不再满足现有的版本的时候,想要一种性能更好ThinkPad,只要这种ThinkPad符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种新版本电脑,都要在工厂类中增加相应的创建业务逻辑(createBMW($type)方法需要新增case),这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类,我们称它为全能类或者上帝类。

我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员。

于是工厂方法模式作为救世主出现了。 工厂类定义成了接口,而每新增的车种类型,就增加该车种类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。

二、工厂方法模式

简介

工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

定义

工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。

组成

1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

使用条件

1)当客户程序不需要知道要使用对象的创建过程。

2)客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。

优点

  • 子类提供挂钩。基类为工厂方法提供缺省实现,子类可以重写新的实现,也可以继承父类的实现。加一层间接性,增加了灵活性
  • 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,只需关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。
  • 多态性:客户代码可以做到与特定应用无关,适用于任何实体类。

缺点

需要Creator和相应的子类作为factory method的载体,如果应用模型确实需要creator和子类存在,则很好;否则的话,需要增加一个类层次。

模拟场景

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的。

类图

图2 工厂方法模式实例类图

实例

Java

//产品类
abstract class ThinkPad{
public ThinkPad() {
//Console.WriteLine("This is ThinkPad!");
}
}
class ThinkPadS1 extends ThinkPad{
public ThinkPadS1() {
System.out.println("This is ThinkPadS1!");
}
}
class ThinkPadS2 extends ThinkPad{
public ThinkPadS2() {
System.out.println("This is ThinkPadS2!");
}
}
package FactoryDemo;
//工厂类
interface FactoryMethod {
ThinkPad GetTypeofThinkPad();
} class FactoryThinkPadS1 implements FactoryMethod {
public ThinkPad GetTypeofThinkPad() {
return new ThinkPadS1();
}
}
class FactoryThinkPadS2 implements FactoryMethod {
public ThinkPad GetTypeofThinkPad() {
return new ThinkPadS2();
}
}
package FactoryDemo;
//客户类
public class Client {
public static void main(String[] args) {
// 工厂方法模式
FactoryThinkPadS1 factory_ThinkPadS1 = new FactoryThinkPadS1();
ThinkPad f_tps1 = factory_ThinkPadS1.GetTypeofThinkPad();
FactoryThinkPadS2 factory_ThinkPadS2 = new FactoryThinkPadS2();
ThinkPad f_tps2 = factory_ThinkPadS2.GetTypeofThinkPad();
}
}

C#

//产品类
public abstract class ThinkPad
{
public ThinkPad()
{
//Console.WriteLine("This is ThinkPad!");
}
}
public class ThinkPadS1 : ThinkPad
{
public ThinkPadS1()
{
Console.WriteLine("This is ThinkPadS1!");
}
}
public class ThinkPadS2 : ThinkPad
{
public ThinkPadS2()
{
Console.WriteLine("This is ThinkPadS2!");
}
}
//工厂类
public interface FactoryMethod
{
ThinkPad GetTypeofThinkPad();
}
public class FactoryThinkPadS1:FactoryMethod
{
public ThinkPad GetTypeofThinkPad()
{
return new ThinkPadS1();
}
}
public class FactoryThinkPadS2 : FactoryMethod
{
public ThinkPad GetTypeofThinkPad()
{
return new ThinkPadS2();
}
}
//客户类:
class Program
{
static void Main(string[] args)
{
// 工厂方法模式
FactoryThinkPadS1 factory_ThinkPadS1 = new FactoryThinkPadS1();
ThinkPad f_tps1 = factory_ThinkPadS1.GetTypeofThinkPad();
FactoryThinkPadS2 factory_ThinkPadS2 = new FactoryThinkPadS2();
ThinkPad f_tps2 = factory_ThinkPadS2.GetTypeofThinkPad();
Console.ReadLine();
}
}

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情 况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。

三、抽象工厂模式

简介

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。根据里氏替换原则,任何接受父类型的地方,都应当能够接受子类型。因此,实际上系统所需要的,仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。

定义

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

组成

1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。

4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。

使用条件

1)系统中有多个产品族,而系统一次只可能消费其中一族产品。

2)同属于同一个产品族的产品一起使用。

优点

  • 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建。
  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  • 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点

增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。

模拟场景

随着客户的要求越来越高,他们需要知道每个版本的显卡和主板型号,ThinkPad需要配置显卡和主板,这个工厂开始生产显卡和主板。这时候工厂有二个系列的产品:显卡和主板。而每个版本的ThinkPad需要配置不同的显卡和主板。这时候分别使用一个显卡工厂和一个主板工厂都不能满足我们的需求,我们必须确认电脑跟配件的对应关系。因此把电脑工厂跟配件工厂联系在一起。因此出现了抽象工厂模式。

类图

图3 抽象工厂模式实例类图

实例

Java

//产品类
abstract class Motherboard{
}
class MotherboardA extends Motherboard{
public MotherboardA() {
System.out.println("This is MotherboardA");
}
}
class MotherboardB extends Motherboard{
public MotherboardB() {
System.out.println("This is MotherboardB");
}
}
class VideoCard{
} class VideoCardA extends VideoCard{
public VideoCardA() {
System.out.println("This is VideoCardA");
}
}
class VideoCardB extends VideoCard{
public VideoCardB() {
System.out.println("This is VideoCardB");
}
}
package FactoryDemo;
//工厂类
interface AbstractFactory{
Motherboard GetMotherboard();
VideoCard GetVideoCard();
} class AbstractFactoryThinkPadS1 implements AbstractFactory
{
public Motherboard GetMotherboard() {
return new MotherboardA();
}
public VideoCard GetVideoCard() {
return new VideoCardA();
}
}
class AbstractFactoryThinkPadS2 implements AbstractFactory{
public Motherboard GetMotherboard(){
return new MotherboardB();
}
public VideoCard GetVideoCard(){
return new VideoCardB();
}
}
package FactoryDemo;
//客户类
public class Client {
public static void main(String[] args) {
// 抽象工厂模式
AbstractFactoryThinkPadS1 aFactory_S1 = new AbstractFactoryThinkPadS1();
aFactory_S1.GetMotherboard();
aFactory_S1.GetVideoCard();
AbstractFactoryThinkPadS2 aFactory_S2 = new AbstractFactoryThinkPadS2();
aFactory_S2.GetMotherboard();
aFactory_S2.GetVideoCard();
}
}

C#

//产品类
public class Motherboard
{
}
public class MotherboardA : Motherboard
{
public MotherboardA()
{
Console.WriteLine("This is MotherboardA");
}
}
public class MotherboardB : Motherboard
{
public MotherboardB()
{
Console.WriteLine("This is MotherboardB");
}
}
public class VideoCard
{
}
public class VideoCardA : VideoCard
{
public VideoCardA()
{
Console.WriteLine("This is VideoCardA");
}
}
public class VideoCardB : VideoCard
{
public VideoCardB()
{
Console.WriteLine("This is VideoCardB");
}
}
//工厂类
public interface AbstractFactory
{
Motherboard GetMotherboard();
VideoCard GetVideoCard();
}
public class AbstractFactoryThinkPadS1 : AbstractFactory
{
public Motherboard GetMotherboard()
{
return new MotherboardA();
}
public VideoCard GetVideoCard()
{
return new VideoCardA();
}
}
public class AbstractFactoryThinkPadS2 : AbstractFactory
{
public Motherboard GetMotherboard()
{
return new MotherboardB();
}
public VideoCard GetVideoCard()
{
return new VideoCardB();
}
}
class Program
{
static void Main(string[] args)
{
//抽象工厂模式
AbstractFactoryThinkPadS1 aFactoryS1 = new AbstractFactoryThinkPadS1();
aFactoryS1.GetMotherboard();
aFactoryS1.GetVideoCard();
AbstractFactoryThinkPadS2 aFactoryS2 = new AbstractFactoryThinkPadS2();
aFactoryS2.GetMotherboard();
aFactoryS2.GetVideoCard();
Console.ReadLine();
}
}

四、总结

首先,我们需要明确使用工厂模式的目的是降低耦合度,所以,当遇到存在分类问题并且类别还会变动的情况下,像创建产品这样,可以考虑是否能有工厂模式。简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象,明确区分了各自的职责和权力,有利于整个软件体系结构的优化,但扩展性不高,耦合度高;当客户程序不需要知道要使用对象的创建过程,或者客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象,可以考虑用工厂方法模式。当系统中有多个产品族,而系统一次只可能消费其中一族产品,或者同属于同一个产品族的产品一起使用,可以考虑用抽象工厂模式。

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

  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. 饭否Oauth记录

    饭否Oauth授权   首先去饭否申请一个应用,创建新应用即可,等待审核.审核通过了之后会拿到consumer_key和consumer_secret.这两个值先记录在代码里.后面经常用到. 然后第一 ...

  2. css3中那些鲜为人知但又很有用的属性

    概述 这是我在写移动端页面的时候遇到的,css3中鲜为人知但又很有用的属性,记录下来,供以后开发时参考,相信对其他人也有用. tap-highlight-color 在移动端开发中,我们需要在用户轻按 ...

  3. sudoer命令各参数含义及设置

    对于普通用户sudo加权的时限制可以执行的命令 https://segmentfault.com/a/1190000007394449 需要修改/etc/sudoers 1. 字段说明如下 授权用户/ ...

  4. C#导出Excel文件Firefox中文件名乱码

    首先说明下:我的解决方法不一定适用于其他遇到该问题的人,因为情况多种多样,适合我的方法不一定适合别人,就像我在遇到问题时查到别人的解决方案放到我的代码里却不管用,所以这个方法仅供参考 这两天做了一个导 ...

  5. WebDriver高级应用实例(8)

    8.1使用Log4j在测试过程中打印日志 目的:在测试过程中,使用Log4j打印日志,用于监控和后续调试测试脚本 被测网页的网址: http://www.baidu.com 环境准备: (1)访问ht ...

  6. 全网最详细的Cloudera Hue执行./build/env/bin/supervisor 时出现KeyError: "Couldn't get user id for user hue"的解决办法(图文详解)

    不多说,直接上干货! 问题详情 如下: [root@bigdata-pro01 hue--cdh5.12.1]# ./build/env/bin/supervisor Traceback (most ...

  7. 配置codis-proxy

    在整个的处理过程之中,虽然利用codis可以帮助我们动态实现主从的配置,但是从实际来讲用户不可能直接去操作redis客户端,必须通过codis-proxy来代理,所以需要针对于codis-proxy进 ...

  8. Redis安装以及主从实现

    Redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串). list(链表).set(集合)和zset(有序 ...

  9. SpringMVC之文件上传

    上传是web程序中常见的功能,当使用上传时,需要把form表单中的enctype属性改为multipart/form-data,这样就使用了二进制进行上传,而后台需要解析这些数据.Dispatcher ...

  10. mongo学习使用记录2 spring data

    spring data mongo 打印mongo NoSql语句 log4j.properties log4j.rootLogger=INFO, stdout log4j.logger.org.sp ...