一,简单的工厂模式
首先举一个例子:
我们现在开了一家饭馆:
public interface Restaurant {
public void cook();
}
然后呢我们的大厨可以做几种菜:
public class Fish implements Restaurant{
@Override
public void cook() {
System.out.println("来一个红烧鱼");
}
}
public class Meet implements Restaurant{
@Override
public void cook() {
System.out.println("炒一盘小炒肉");
}
}
下面客人进场,开始点餐:
public class Customer {
public static void main(String args[]) {
Restaurant restaurant = new Meet();
restaurant.cook();
Restaurant restaurant2 = new Fish();
restaurant2.cook();
}
}
我们观察上面的代码,虽然很好的完成了任务,但是,我们的实现类和和接口紧密的绑定到了一起,这意味着我们的代码耦合出现严重问题,不利于以后的维护,试想顾客点餐需要与后厨大厨直接接触,这肯定是一个不好的体验,那么我们就需要一个传菜员或者一个点餐系统:
public class Waiter {
public static final int MENU_MEET = 1;
public static final int MENU_FISH = 2;
public static Restaurant chooseMenu(int menu) {
switch(menu) {
case MENU_FISH:
return new Fish();
case MENU_MEET:
return new Meet();
default:return new Meet();
}
}
}
这个时候,客人再点餐的话就可以直接找到该服务员,让他负责跟后厨沟通:
public class Customer2 {
public static void main(String args[]) {
Restaurant restaurant = Waiter.chooseMenu(1);
restaurant.cook();
}
}
这个时候,我们只需要chooseMenu方法直接告知我们需要什么就行了。
特点
1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。
2 create()方法通常是静态的,所以也称之为静态工厂。
缺点
1 扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)
2 不同的产品需要不同额外参数的时候 不支持。
二:多方法静态工厂(常用)
我们知道,上面的简单工厂模式有一个缺点是不同的产品需要不同的额外参数的时候,是不支持的,
而且如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。
而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。
请看下面的例子。
同样是一接口:
public interface Restaurant {
public void cook();
}
实现类(产品)
public class Fish implements Restaurant{
@Override
public void cook() {
System.out.println("来个红烧鱼");
}
}
public class Meet implements Restaurant{
String param;
public Meet(String param) {
this.param = param;
}
@Override
public void cook() {
System.out.println("来盘小抄肉"+param);
}
}
工厂类:
public class CookFactory {
public static Restaurant createMeet(String param) {
return new Meet(param);
}
public static Restaurant createFish() {
return new Fish();
}
}
使用 :
public class MainClass {
public static void main(String args[]) {
Restaurant meetCooker = CookFactory.createMeet("七分熟");
meetCooker.cook();
Restaurant fishCooker = CookFactory.createFish();
fishCooker.cook();
}
}
三:工厂方法模式
工厂方法模式是把普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。
为了解决简单工厂的问题,程序员们又想出来一个新的办法,就是设计一个工厂的接口,你想要什么东西,就写个类继承于这个工厂,这样就不用修改什么,直接添加就行了。就相当于,我这个工厂是用来生产鞋子的,而要什么品牌的鞋子具体分到了每个车间,如果新多了一种品牌的鞋子,直接新增一个车间就行了。那么问题又来了,如果想要生产衣服怎么办?
还是那个Restanrant接口和其实现类(产品)这里不再重复贴出了,我们要看工厂类的实现
抽象工厂类:
public interface abstractCookFactory {
public Restaurant createREstaurant();
}
其实现类(获取具体产品)
public class FishFactory implements abstractCookFactory{
@Override
public Restaurant createREstaurant() {
return new Fish();
}
}
public class MeetFactory implements abstractCookFactory {
@Override
public Restaurant createREstaurant() {
return new Meet("九分熟");
}
}
开始享用:
public class MainClass {
public static void main(String args[]) {
Restaurant meet = new FishFactory().createREstaurant();
meet.cook();
Restaurant fish = new MeetFactory().createREstaurant();
meet.cook();
}
}
具体说明工厂方法模式:
通过上面的一个代码案例我们大概的了解了一下工厂方法模式,下面我们具体了解。
在上文提到的最易懂的设计模式系列解析:简单工厂模式,发现简单工厂模式存在一系列问题:
-
工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
-
违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
-
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
为了解决上述的问题,我们又使用了一种新的设计模式:工厂方法模式。
1. 介绍
1.1 定义
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
1.2 主要作用
将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。
1.3 解决的问题
- 工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了“开放 - 关闭原则 即简单工厂模式的缺点
- 之所以可以解决简单工厂的问题,是因为工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点
2. 模式原理
2.1 UML类图
2.3 使用步骤
步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实
3. 实例讲解
接下来我用一个实例来对工厂方法模式进行更深一步的介绍。
3.1 实例概况
背景:小成有一间塑料加工厂(仅生产A类产品);随着客户需求的变化,客户需要生产B类产品;
冲突:改变原有塑料加工厂的配置和变化非常困难,假设下一次客户需要再发生变化,再次改变将增大非常大的成本;
解决方案:小成决定置办塑料分厂B来生产B类产品;
即工厂方法模式
3.2 使用步骤
步骤1: 创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
public abstract Product Manufacture();
}
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
abstract class Product{
public abstract void Show();
}
步骤3: 创建具体产品类(继承抽象产品类), 定义生产的具体产品;
//具体产品A类
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}
//具体产品B类
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生产出了产品B");
}
}
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
//工厂A类 - 生产A类产品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}
//工厂B类 - 生产B类产品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}
**步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
**
//生产工作流程
public class FactoryPattern {
public static void main(String[] args){
//客户要产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();
//客户要产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}
结果:
生产出了产品A
生产出了产品C`
4. 优点
- 更符合开-闭原则
新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
简单工厂模式需要修改工厂类的判断逻辑
- 符合单一职责原则
每个具体工厂类只负责创建对应的产品
简单工厂中的工厂类存在复杂的switch逻辑判断
- 不使用静态工厂方法,可以形成基于继承的等级结构。
简单工厂模式的工厂类使用静态工厂方法
总结:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
5. 缺点
-
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
-
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
-
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
-
一个具体工厂只能创建一种具体产品
三 抽象工厂模式
抽象工厂模式(别名配套):提供一个创建一系列或相互依赖对象的接口,而无须指定它们具体的类。
抽象工厂模式的结构中包括4种角色:
-
1.抽象产品(Product):抽象产品是一个抽象类或接口,负责定义具体产品必须实现的方法。
-
2.具体产品(ConcreteProduct):具体产品是一个类,如果Product是一个抽象类,那么具体产品是Product的子类;如果Product是一个接口,那么具体产品是实现Product接口的类。
-
3.抽象工厂(AbstractFactory):抽象工厂是一个接口或抽象类,负责定义若干个抽象方法。
-
4.具体工厂(ConcreteFactory):如果抽象工厂是抽象类,具体工厂就是抽象工厂的子类;如果抽象工厂是接口,具体工厂是实现抽象工厂的类。具体工厂覆写抽象工厂中的抽象方法,使该方法返回具体产品的实例
UML类图如下:
抽象工厂模式与工厂方法模式的区别
- 抽象工厂模式是工厂方法模式的升级版,它用来创建一组相关或者相互依赖的对象。
-
它与工厂方法模式的区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品等级结构,表现为一个接口或抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类,如上图中的ProductA和ProductB。
-
在抽象工厂模式中,有一个产品族的概念,所谓产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成了一个产品族;而工厂方法提供的一系列产品称为一个等级结构。如上图中的ProductA和ProductB称为两个不同的等级结构,ProductA1和ProductA2属于同一个等级结构,ProductB1和ProductB2属于另一个等级结构,而ProductA1和ProductB1属于同一个产品族,ProductA1和ProductB1同属于一个产品族。
-
如果产品全部属于同一个等级结构(即同一个接口或抽象类),则属于工厂方法模式;如果产品来自多个等级结构(即多个不同的接口或抽象类),则属于抽象工厂模式
抽象工厂模式的优点
- 1.抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。产品族一般或多或少的都存在一定的关联,抽象工厂模式就可以在类的内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
-
2.抽象工厂模式可以为用户创建一系列相关的对象,使得用户和创建这些对象的类脱耦。
-
3.使用抽象工厂模式可以方便的为用户配置一系列对象。用户使用不同的具体工厂就能得到一组相关的对象,同时也能避免用户混用不同系列中的对象。
-
4.在抽象工厂模式中,可以随时增加“具体工厂”为用户提供一组相关的对象。
适合使用抽象工厂模式的情景:
- 1.系统需要为用户提供多个对象,但不希望用户直接使用new运算符实例化这些对象,即希望用户和创建对象的类脱耦。
-
2.系统需要为用户提供多个相关的对象,以便用户联合使用它们,但又不希望用户来决定这些对象是如何关联的。
-
3.系统需要为用户提供一系列对象,但只需要用户知道这些对象有哪些方法可用,不需要用户知道这些对象的创建过程。
-
4.在一个继承体系中,如果存在多少个等级结构(即存在多个抽象类或接口),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,会更适合。