如果盗用网站模板,朝阳市做网站的公司,简单网站建设软件,网站数据库迁移今天开始这个系列之前#xff0c;心里有些恐慌#xff0c;毕竟园子里的高手关于设计模式的经典文章很多很多#xff0c;特别是大侠李会军、吕震宇 老师的文章更是堪称经典。他们的文笔如行云流水#xff0c;例子活泼生动#xff0c;讲解深入浅出。好在他们都是用C#描述心里有些恐慌毕竟园子里的高手关于设计模式的经典文章很多很多特别是大侠李会军、吕震宇 老师的文章更是堪称经典。他们的文笔如行云流水例子活泼生动讲解深入浅出。好在他们都是用C#描述也没有提供必要的源码下载所以我这里用C实 现。首先我想声明的是我的文笔绝对不如他们的好例子也没有他们的形象不过我打算把C的代码实现和类图提供给大家就算作为一种补充吧。 开始设计模式自然而然到提到几个原则I、开闭法则OCPII、里氏代换法则LSPIII、依赖倒置法则(DIP)IV、接口隔离法则ISPV、合成/聚合复用原则CARPVI、迪米特法则LoD这几个法则在吕震宇 老师的设计模式二和设计模式三中有非常详尽的阐述和深入浅出的举例分析。有兴趣的朋友打开链接看一下就可以了。 补充说明 我这里所以代码都是用VS2005的C编译器实现。所以不能保证在其他IDE中能顺利编译但是我想如果你使用其他编译器也应该不会有太大问题主要也应该是stdafx.h文件中包含的头文件问题。里面出行的结构图都是用微软的Visio2003 绘制大家下载后可以直接用Visio打开。在 以后所有的模式例子中都有客户程序客户程序这个角色不是模式本身的内容它是模式之外的部分但是正是这个客户程序完成了对模式的使用模式本身的结构 是讲解的重点但是客户程序如何使用模式也是理解模式的一个重要方面因此在我后续的介绍中都有客户程序这个角色并会说明究竟调用模式中的哪些角色完成 对模式的使用。
简单工厂模式生活例子 吃饭是人的基本需求如果人类不需要吃饭可能我们就能活得清闲许多也就不需要像现在一样没日没夜的工作学习。我们学习是为了找到更好的工作好工作 为了赚更多的钱最终为了吃饱饭吃好饭。因此可以说吃饭是与人息息相关下面就从吃饭的例子来引入工厂模式的学习。 如果你想吃饭了怎么办自己做吗自己做就相当于程序中直接使用new。当然是自己下个指令别人来做更爽。那就把做饭的任务交给你的老婆吧那么她就是 一个做饭的工厂了你告诉她要要吃红烧肉等会她就从厨房给你端出来一盘香喷喷的红烧肉了再来个清蒸鱼吧大鱼大肉不能太多那就再来个爆炒空心菜最 后再来个西红柿鸡蛋汤。下图 就是这个问题的模型。 显然到了这里你是Client你老婆就是工厂她拥有做红烧肉的方法做清蒸鱼的方法做爆炒空心菜、西红柿鸡蛋汤的方法这些方法返回值就是食物 抽象。红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤就是食物的继承类到这里你就可以大吃二喝了。简单工厂模式也成型了。哈哈娶一个手艺不错的老婆还真 好吃的好吃的爽又清闲。 下面来看标准的简单工厂模式的分析。 意图 把一系列拥有共同特征的产品的创建封装 结构图 图2 角色分析产品基类 工厂创建的所有产品的基类, 它负责描述所有实例所共有的公共接口。它用来作为工厂方法的返回参数。 代码实现 //---这时一个系列的产品基类
class Product
{
protected:Product(void);
public:virtual ~Product(void);
public:virtual void Function() 0;
};
//cpp
Product::Product(void)
{
}
Product::~Product(void)
{
}
具体产品类产品1和产品2这个角色实现了抽象产品角色所定义的接口。 代码实现 //产品A
class ConcreteProductA :public Product
{
public:ConcreteProductA(void);
public:virtual ~ConcreteProductA(void);
public:virtual void Function();
};
//cpp
ConcreteProductA::ConcreteProductA()
{cout 创建 A 产品 endl;
}
ConcreteProductA::~ConcreteProductA()
{cout 释放 A 产品 endl;
}
void ConcreteProductA::Function()
{cout 这是产品 A 具有的基本功能 endl;
} //产品B与A类似不这里不再给出大家可以下载源码工厂类负责具体产品的创建有两种方式实现产品的创建I、创建不同的产品用不同的方法II、创建不同产品用相同的方法然后通过传递参数实现不同产品的创建。本实例中两种模式都给出了大家自行分析。 //简单工厂此类不需要继承直接硬编码实现生成的产品
class SimpleFactory
{
public:SimpleFactory() {}
public:~SimpleFactory() {}
public:Product *CreateProduct(int ProuctType);Product *CreateProductA();Product *CreateProductB();
};
//CPP
Product * SimpleFactory::CreateProduct(int ProductType 0)
{Product *p 0;switch (ProductType){case 0:p new ConcreteProductA();break;case 1:p new ConcreteProductB();break;default:p new ConcreteProductA();break;}return p;
}
Product *SimpleFactory::CreateProductA()
{return new ConcreteProductA();
}
Product *SimpleFactory::CreateProductB()
{return new ConcreteProductB();
}
客户端程序访问的角色包括产品基类、工厂类。不直接访问具体产品类。通过基类指针的多态实现产品功能的调用。访问描述客户程序通过调用工厂的方法返回抽象产品然后执行产品的方法。 //调用代码
SimpleFactory sf;
Product *p sf.CreateProductA();
p-Function();
delete p;
p sf.CreateProductB();
p-Function();
delete p;
优缺点说明优点 1) 首先解决了代码中大量New的问题。为何要解决这个问题好处的说明我想放到结尾总结中。 2) 用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。 缺点对修改不封闭新增加产品您要修改工厂。违法了鼎鼎大名的开闭法则OCP。 附加说明
大家可以参看 吕震宇 老师的C#设计模式四参看这个模式的分析里面还给出了这个模式的两个变体实现比较简单有兴趣的朋友可以自行用C实现一下。产品基类的代码中构造函数我用了Protected而没有使用Public主要是为了体现编码中的一个最小权限原则。说明此类不许用户直接实例化。虽然 这里使用了virtual void Function() 0;编译器也会控制不让用户直接实例化不过我依然认为使用私有化构造函数来保护类不直接实例化是一个良好的编程风格。工厂方法模式生活例子: 人是最贪得无厌的动物老婆手艺再好总有不会做的菜你想吃回锅肉怎么办让老婆学呗于是就给她就新增了做回锅肉的方法以后你再想吃一个新菜就 要给你老婆新加一个方法显然用老婆做菜的缺点也就暴露出来了用程序设计的描述就是对修改永远不能封闭。当然优点也是有的你有了老婆这个工厂这些菜 不用你自己做了只要直接调用老婆这个工厂的方法就可以了。 面对上面对修改不能封闭的问题有没有好的解决方案吗如果你有钱问题就迎刃而解了把老婆抽象变成一个基类你多娶几个具体的老婆分别有做鱼 的做青菜的炖汤的老婆如果你想吃一个新菜就再新找个女人从你的老婆基类继承一下让她来做这个新菜。显然多多的老婆这是所有男人的梦想没有办法法律不允许那么咱们只是为了做饭老婆这个抽象类咱们不叫老婆了叫做厨师吧她的子类也自然而然的该叫做鱼的厨师、炖汤的厨师了。现在来看这个模式发生了变化结构中多了一个厨师的抽象抽象并不具体的加工产品了至于是炖汤还是炖鱼是由这个抽象工厂的继承子类来实现现在的模式也就变成工厂方法模式了这个上面的结构图就变成了下图的结构了。 现在再来分析现在的模式显然简单工厂的缺陷解决了新增加一个菜只需要新增加一个厨师就行了原来的厨师还在做原来的工作这样你的设计就对修改封 闭了。你看把老婆解放出来招聘大量的厨师到你家里这个方案多么的完美你老婆也会爱死你了。当然前提就是你要有多多的钱噢当然这里的钱的多少在软件领域应该看你的客户软件投资方的要求。 下面来一下标准的工厂模式的实现意图 定义一个用户创建对象的接口让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。上面是GOF关于此模式的意图描述我想补充的是您可以这样理解为了改善简单工厂对修改不能关闭的问题。
结构 图4 角色分析产品基类同简单工厂的产品基类其实就是用和简单工厂中的是同一个类这里并没有重写。具体产品类也是用的简单工厂的具体产品类为了体现对修改的关闭这里为系统新添加了一个具体产品类就是“新产品”代码中叫做“ConcreteProductANew”工厂基类定义了工厂创建产品的接口但是没有实现具体创建工作由其继承类实现。 代码实例 //工厂模式此模式的工厂只定义加工产品的接口具体生成交予其继承类实现 //只有具体的继承类才确定要加工何种产品
class Factory
{
public:Factory(void);
public:virtual ~Factory(void);
public:virtual Product* CreateProduct(int ProductType 0) 0;
};
//CPP
Factory::Factory(void)
{
}
Factory::~Factory(void)
{
}
具体工厂类工厂基类的具体实现由此类决定创建具体产品这里 ConcreteFactory1 对于与图中的 工厂实现ConcreteFactory2 对于与图中的新工厂。 下面给出实现代码
//工厂实现
class ConcreteFactory1 :public Factory
{
public:ConcreteFactory1();
public:virtual ~ConcreteFactory1();
public:Product* CreateProduct(int ProductType);
};
//新工厂当要创建新类是实现此新工厂
class ConcreteFactory2 :public Factory
{
public:ConcreteFactory2();
public:virtual ~ConcreteFactory2();
public:Product* CreateProduct(int ProductType);
};
//CPP
ConcreteFactory1::ConcreteFactory1()
{
}
ConcreteFactory1::~ConcreteFactory1()
{
}
Product * ConcreteFactory1::CreateProduct(int ProductType 0)
{Product *p 0;switch (ProductType){case 0:p new ConcreteProductA();break;case 1:p new ConcreteProductB();break;default:p new ConcreteProductA();break;}return p;
}
ConcreteFactory2::ConcreteFactory2()
{
}
ConcreteFactory2::~ConcreteFactory2()
{
}
Product * ConcreteFactory2::CreateProduct(int ProductType 0)
{return new ConcreteProductANew();
}
客户端调用访问角色产品基类、工厂基类、工厂实现类调用描述客户程序通过工厂基类的方法调用工厂实现类用来创建所需要的具体产品。从而实现产品功能的访问。 代码实现
Factory*fct new ConcreteFactory1();
Product *p fct-CreateProduct(0);
p-Function();
delete p;
p fct-CreateProduct(1);
p-Function();
delete p;
delete fct;
fct new ConcreteFactory2();
p fct-CreateProduct();
delete p;
delete fct; 优缺点分析优点
简单工厂具有的优点解决了简单工厂的修改不能关闭的问题。系统新增产品新增一个产品工厂即可对抽象工厂不受影响。
缺点对于创建不同系列的产品无能为力 适用性
当一个类不知道它所必须创建的对象的类的时候。当一个类希望由它的子类来指定它所创建的对象的时候。当类将创建对象的职责委托给多个帮助子类中的某一个并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
其他参考
吕震宇的C#设计模式5Factory Method PatternTerryLee 的.NET设计模式5工厂方法模式Factory Method抽象工厂模式生活例子世事多变随着时间的推移走过的地方越来越多你天南海北的朋友也越来越多。你发现菜原来还分了许多菜系鲁菜、粤菜、湘菜等等它们各有各的风味同样是红烧肉由不同菜系出来的味道也各不相同 你招待不同的朋友要用不同的菜系这下难办了你的厨师都是鲁菜风味怎么办广东的朋友来了吃不惯。现在我们再回到简单工厂模式就是老婆做菜的模式我们把红烧肉再向下继承生成鲁菜红烧肉、粤菜红烧肉、湘菜红烧肉清蒸鱼向下继承为鲁菜清蒸鱼、粤菜清蒸鱼、湘菜清蒸鱼其它也以此类推。我们也 修改一下老婆的这个类不让其返回食物基类而是返回红烧肉、清蒸鱼、爆炒空心菜、西红柿鸡蛋汤这一层次并把这些方法抽象化作为菜系工厂基类然后再 从此基类继承出鲁菜工厂、粤菜工厂、湘菜工厂等等再由这些具体工厂实现创建具体菜的工作哈哈你如果招待广东朋友就用粤菜工厂返回的就是一桌粤菜菜 系的红烧肉、清蒸鱼、空心菜和西红柿鸡蛋汤了你的广东朋友一定会吃的非常合乎胃口了。噢非常好你已经实现了抽象工厂模式了。结构模型图也变成了下图 6)的样子了。 图6 现在可以看到想新来做一个菜系只需新聘请一个厨师就可以了多么完美但是你先别高兴太早如果你想新增加一个菜就变得非常困难了。 意图 提供一个创建一系列相关或相互依赖对象的接口而无需指定它们具体的类。 结构 角色分析产品基类这里包含产品基类A和产品基类B实际上在我的示例代码中这两个产品都从共同的基类继承而来但是这个继承关系却是在这个模式之外的部分而本身这个模式关心的是这两个产品基类的差异部分。 代码实现这里的代码就是借用的简单工厂模式中具体产品类的代码实现部分为了大家阅读方便下面重新给出一下。
//产品A
class ConcreteProductA :public Product
{
public:ConcreteProductA(void);
public:virtual ~ConcreteProductA(void);
public:virtual void Function();
};
//cpp
ConcreteProductA::ConcreteProductA()
{cout 创建 A 产品 endl;
}
ConcreteProductA::~ConcreteProductA()
{cout 释放 A 产品 endl;
}
void ConcreteProductA::Function()
{cout 这是产品 A 具有的基本功能 endl;
} //产品B与A类似不这里不再给出大家可以下载源码具体产品类这里的具体产品类是产品A1A2B1、B2等 代码实现A1对应的实现就是“”
class ConcreteProductA1 :public ConcreteProductA
{
public:ConcreteProductA1(void);
public:virtual ~ConcreteProductA1(void);
public:virtual void Function();
};
//CPP
ConcreteProductA1::ConcreteProductA1()
{cout 创建 A1 产品 endl;
}
ConcreteProductA1::~ConcreteProductA1()
{cout 释放 A1 产品 endl;
}
void ConcreteProductA1::Function()
{cout 这时产品 A1 具有的基本功能 endl;
}
工厂抽象接口定义了创建产品的接口这里返回参数是返回的产品A产品B而本身产品A和B的共同基类小弟认为正是这个特征构成了抽象工厂和工厂模式的区别。 代码实现
//抽象工厂模式
class AbstractFactory
{
public:AbstractFactory();
public:virtual ~AbstractFactory();
public:virtual ConcreteProductA* CreateA() 0;virtual ConcreteProductB* CreateB() 0;
};
//CPP
AbstractFactory::AbstractFactory()
{
}
AbstractFactory::~AbstractFactory()
{
} 具体工厂实现类工厂1和工厂2。新增加系列只需新实现一个工厂。 代码实现: 工厂1的就是ConcreteAbsFactory1工厂2的代码类似这里没有给出可以在下载代码中看到
工厂1-----
class ConcreteAbsFactory1 :public AbstractFactory
{
public:ConcreteAbsFactory1();
public:virtual ~ConcreteAbsFactory1();
public:virtual ConcreteProductA* CreateA();virtual ConcreteProductB* CreateB();
};
//CPP
ConcreteAbsFactory1::ConcreteAbsFactory1()
{
}
ConcreteAbsFactory1::~ConcreteAbsFactory1()
{
}
ConcreteProductA* ConcreteAbsFactory1::CreateA()
{return new ConcreteProductA1();
}
ConcreteProductB * ConcreteAbsFactory1::CreateB()
{return new ConcreteProductB1();
} 客户端访问 访问角色产品基类、抽象工厂、具体工厂实现类 访问描述 通过抽象工厂的指针访问具体工厂实现来创建对应系列的产品然后通过产品基类指针访问产品功能。 调用代码
AbstractFactory *absfct new ConcreteAbsFactory1();
ConcreteProductA *cpa absfct-CreateA();
cpa-Function();
delete cpa;
ConcreteProductB *cpb absfct-CreateB();
cpb-Function();
delete cpb;
delete absfct;
absfct new ConcreteAbsFactory2();
cpa absfct-CreateA();
cpa-Function();
delete cpa;
cpb absfct-CreateB();
cpb-Function();
delete cpb; 和工厂模式的分析比较 现在可以和工厂模式对比一下抽象工厂返回的接口不再是产品A和产品B的共同基类Product了而是产品A、产品B基类在工厂模式中它们为具体实现 类这里变成了基类了。此时工厂的抽象和简单工厂中的工厂方法也很类似就是这些特征区使其别于工厂模式而变成抽象工厂模式了因此抽象工厂解决的是创 建一系列有共同风格的产品鲁菜还是粤菜而工厂方法模式解决的创建有共同特征的一系列产品红烧肉、清蒸鱼它们都是食物。当然简单工厂的缺陷在抽象 工厂中又再次出现了我要新增加一个产品工厂抽象接口就要改变了。因此抽象工厂并不比工厂模式完美只不过是各自的适用领域不同而已。其实这里如果把 抽象工厂模式的接口返回产品A和产品B的共同基类工厂模式返回的参数你会发现奇怪这个模式怎么这么眼熟它不是恰恰退化成工厂模式了。 类模式与对象模式的区别讨论先看定义类“模式使用继承关系把对象的创建延迟的子类对象模式把对象的创建延迟到另一个对象中”。 分析首先它们创建对象都不是在基类中完成都是在子类中实现因此都符合类模式的概念但是工厂模式的创建产品对象是在编译期决定的要调用某个工厂固 定的而抽象工厂模式对产品的创建是在运行时动态决定的只有到运行时才确定要调用那个工厂调用工厂随运行环境而改变。这里我一直很混乱欢迎大家讨 论 适用性
一个系统要独立于它的产品的创建、组合和表示时一个系统要由多个 产品系列中的一个来配置时当你要强调一个系列相关的产品对象的设计以便进行联合使用时当你提供一个产品类库而只想显示它们的接口而不是实现时。
参考
吕震宇的C#设计模式6TerryLee 的.NET设计模式3抽象工厂模式Abstract Factory
总结 工厂本质就是用工厂方法替代直接New来创建对象。这里不是指的让用户重载一个新操作符号来进行创建对象的操作而是说把New 操作封装在一个方法中等用户需要创建对象时调用此方法而避免直接使用New而已。这样做的目的就是之一就是封装避免代码中大量New的运算符这当然 不是主要目的因为这样虽然New少了CreateObject方法却多了但是如果产品类的构造函数变了我想常用工厂模式的修改源代码的工作应该简 便许多吧当然这算不上这个模式的好处它的真正强大的功能其实在于适应变化这也是整个设计模式最根本的目的还有一点就是体现了抽象于实现的分离当 然创建型模式都具有这个特点工厂模式非常明显吧了把具体创建工作放置到工厂中使客户端程序更专注与业务逻辑的这样的代码结构也更进行合理。