设计模式--工厂模式

1、工厂模式

1.1 引言

1)还没有工厂时代:假如还没有工业革命,如果一个客户要一款宝马车,一般的做法是客户去创建一款宝马车,然后拿来用。

2)简单工厂模式:后来出现工业革命。用户不用去创建宝马车。因为客户有一个工厂来帮他创建宝马.想要什么车,这个工厂就可以建。比如想要320i系列车。工厂就创建这个系列的车。即工厂可以创建产品。

3)工厂方法模式时代:为了满足客户,宝马车系列越来越多,如320i,523i,30li等系列一个工厂无法创建所有的宝马系列。于是由单独分出来多个具体的工厂。每个具体工厂创建一种系列。即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象。你需要指定某个具体的工厂才能生产车出来。

4)抽象工厂模式时代:随着客户的要求越来越高,宝马车必须配置空调。于是这个工厂开始生产宝马车和需要的空调。

最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车.这就是工厂模式。


1.2工厂模式简介

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

工厂模式在《Java与模式》中分为三类:

1)简单工厂模式(Simple Factory):不利于产生系列产品;

2)工厂方法模式(Factory Method):又称为多形性工厂;

3)抽象工厂模式(Abstract Factory):又称为工具箱,产生产品族,但不利于产生新的产品;

这三种模式从上到下逐步抽象,并且更具一般性。GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。


1.3 为什么要用工厂模式及它的作用是什么?(参考来源:创建对象与使用对象——谈谈工厂的作用

与一个对象相关的职责通常有三类:对象本身所具有的职责创建对象的职责使用对象的职责

对象本身的职责:就是对象自身所具有的一些数据和行为,可通过一些公开的方法来实现它的职责。

在Java语言中,我们通常有以下几种创建对象的方式

(1) 使用new关键字直接创建对象;
(2) 通过反射机制创建对象;
(3) 通过clone()方法创建对象;
(4) 通过工厂类创建对象。

在客户端代码中直接使用new关键字是最简单的一种创建对象的方式,但是它的灵活性较差,下面通过一个简单的示例来加以说明:

class LoginAction {  
    private UserDAO udao;  
      
    public LoginAction() {  
        udao = new JDBCUserDAO(); //创建对象  
    }  
      
    public void execute() {  
        //其他代码  
        udao.findUserById(); //使用对象  
        //其他代码  
    }  
} 
问题:
LoginAction类负责创建了一个UserDAO子类的对象并使用UserDAO的方法来完成相应的业务处理,也就是说LoginAction即负责udao的创建又负责udao的使用,创建对象和使用对象的职责耦合在一起,这样的设计会导致一个很严重的问题:如果在LoginAction中希望能够使用UserDAO的另一个子类如HibernateUserDAO类型的对象,必须修改LoginAction类的源代码,违反了“开放封闭原则”

如何解决该问题?
最常用的一种解决方法是将udao对象的创建职责从LoginAction类中移除,在LoginAction类之外创建对象,那么谁来负责创建UserDAO对象呢?答案是:工厂类。通过引入工厂类,客户类(如LoginAction)不涉及对象的创建,对象的创建者也不会涉及对象的使用

工厂模式的作用:将对象的创建和使用分离,也使得系统更加符合“单一职责原则”,有利于对功能的复用和系统的维护。


1.4 是否需要为设计中的每一个类都配备一个工厂类?

答案是:具体情况具体分析。如果产品类很简单,而且不存在太多变数,其构造过程也很简单,此时无须为其提供工厂类,直接在使用之前实例化即可,例如Java语言中的String类,我们就无须为它专门提供一个StringFactory,这样做反而有点像杀鸡用牛刀,大材小用,而且会导致工厂泛滥,增加系统的复杂度。


2、简单工厂模式

2.1 简单工厂模式介绍

简单工厂模式又称静态工厂方法模式。它存在的目的很简单:定义一个用于创建对象的接口。
在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定那一个产品类应当被实例化, 如同一个交通警察站在来往的车辆流中,决定放行那一个方向的车辆向那一个方向流动一样。
先来看看它的组成:
1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
3) 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

2.1 UML类图


2.3简单工厂模式的实现

抽象产品:

//抽象产品
public abstract class Product {
	
    public abstract void Opration();

}

具体产品:

public class ProductA extends Product {

	@Override
	public void Opration() {
		System.out.println("ProductA Opration");
	}

}

public class ProductB extends Product {

	@Override
	public void Opration() {
		System.out.println("ProductB Opration");
	}

}

简单工厂类:

//简单工厂类
public class SimpleProductFactory {

	private static Product product = null;

	public static Product CreateProduct(String productType) {

		switch (productType) {
		case "A":
			product = new ProductA();
			break;
		case "B":
			product = new ProductB();
			break;
		}

		return product;
	}
客户端类:

public class ClientTest {
	
	public static void main(String[] args) {
		Product product = SimpleProductFactory.CreateProduct("A");
		product.Opration();
	}
}


优点:
1 职责单一,实现简单,且实现了客户端代码与具体实现的解耦。
2 工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.
3 通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.
4 明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点:
1 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。因此它是违背开放封闭原则的。
2 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
注:这些缺点在工厂方法模式中得到了一定的克服。
使用场景:
1 工厂类负责创建的对象比较少;
2 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
3 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。


问题:

对Product部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种新Product,都要在工厂类中增加相应的创建业务逻辑,这显然是违背开闭原则的。

2.4 反射实现简单工厂模式

前面的抽象产品,具体产品代码不需要改变。

简单工厂类(加反射):

//简单工厂类
public class SimpleProductFactory {

	public static Product CreateProduct(String productName) {

		try {
			Class c = Class.forName(productName);
			return (Product) c.newInstance();
		} catch (ClassNotFoundException e) {
			System.out.println("This class doesn't exsist!");
			e.printStackTrace();
		} catch (InstantiationException e) {
			System.out.println("This class can't be instantiated!");
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}
}

客户端类:

public class ClientTest {
	
	public static void main(String[] args) {
		Product product = SimpleProductFactory.CreateProduct("FactoryPattern.ProductA");
		product.Opration();
	}
}

这样就不用每次增加新的Product都要修改Factory了!不过每次获取Product对象时SimpleProductFactory.CreateProduct("FactoryPattern.ProductA")都要写获取对象的完整包名,用配置文件的方式可以解决不少问题!


问题:

由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的工厂类累坏了,也累坏了我们这些程序员。于是工厂方法模式作为救世主出现了。 工厂类定义成了接口,而每新增新的类型的Product,就增加该类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。


3、工厂方法模式 

3.1 工厂方法模式介绍
工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
来看下它的组成:
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

3.2 UML类图


3.3 工厂方法模式的实现

前面的抽象产品,具体产品代码不需要改变。

抽象工厂类:

//抽象工厂
public abstract class AbstractProductFactory {

	public abstract Product createProduct();

}
具体工厂类:

public class ProductAFactory extends AbstractProductFactory {
	@Override
	public ProductA createProduct() {
		return new ProductA();
	}
}

public class ProductBFactory extends AbstractProductFactory {
	@Override
	public ProductB createProduct() {
		return new ProductB();
	}
}
客户端类:

public class ClientTest {
	
	public static void main(String[] args) {
		ProductAFactory productAFactory = new ProductAFactory();
		ProductA productA = productAFactory.createProduct();
		productA.Opration();
	}
}

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

问题:

可以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。所以我建议在这种情况下使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。当然特殊的情况,就要特殊对待了:对于系统中存在不同的产品树,而且产品树上存在产品族。那么这种情况下就可能可以使用抽象工厂模式了。


启迪:如果不使用工厂模式来实现我们的例子,也许代码会减少很多——只需要实现已有的产品,不使用多态。但是在可维护性上,可扩展性上是非常差的(你可以想象一下添加一个产品后要牵动的类)。因此为了提高扩展性和维护性,多写些代码是值得的。


4、抽象工厂模式

4.1 抽象工厂模式介绍

先来认识下什么是产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族。还是让我们用一个例子来形象地说明一下吧。



图中的BmwCar和BenzCar就是两个产品树(产品层次结构);而如图所示的BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。同理BmwBussinessCar和BenzSportsCar也是一个产品族。

回到抽象工厂模式的话题上。
可以说,抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。
抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象
 
而且使用抽象工厂模式还要满足一下条件: 
1)系统中有多个产品族,而系统一次只可能消费其中一族产品。 
2)同属于同一个产品族的产品以其使用。
 
来看看抽象工厂模式的各个角色(和工厂方法的如出一辙):
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

4.2 UML类图


4.3 抽象工厂具体实现

抽象产品:

abstract class BenzCar{  
    private String name;  
      
    public abstract void drive();  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
} 


abstract class BmwCar{  
    private String name;  
      
    public abstract void drive();  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  

具体产品:

class BenzSportCar extends BenzCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BenzSportCar-----------------------");  
    }  
}  


class BenzBusinessCar extends BenzCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BenzBusinessCar-----------------------");  
    }  
}  


class BmwSportCar extends BmwCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BmwSportCar-----------------------");  
    }  
} 

 
class BmwBusinessCar extends BmwCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BmwBusinessCar-----------------------");  
    }  
}  

抽象工厂:

abstract class Driver{  
    public abstract BenzCar createBenzCar(String car) throws Exception;  
      
    public abstract BmwCar createBmwCar(String car) throws Exception;  
       
} 
具体工厂:

//具体工厂  
class SportDriver extends Driver{  
    public BenzCar createBenzCar(String car) throws Exception {  
        return new BenzSportCar();  
    }  
    public BmwCar createBmwCar(String car) throws Exception {  
        return new BmwSportCar();  
    }  
}

  
class BusinessDriver extends Driver{  
    public BenzCar createBenzCar(String car) throws Exception {  
        return new BenzBusinessCar();  
    }  
    public BmwCar createBmwCar(String car) throws Exception {  
        return new BmwBusinessCar();  
    }
}  

客户端类:

public class ClientTest {  
  
    public static void main(String[] args) throws Exception {  
          
        Driver d = new BusinessDriver();  
        BmwCar car = d.createBmwCar("");  
        car.drive();  
    }  
} 

其中:BenzSportCar和BenzBusinessCar属于产品树;同理BmwSportCar和BmwBusinessCar。而BenzSportCar和BmwSportCar和AudiSportCar属于产品族。
所以抽象工厂模式一般用于具有产品树和产品族的场景下
抽象工厂模式的缺点:如果需要增加新的产品树,那么就要新增三个产品类,比如VolvoCar,VolvoSportCar,VolvoSportCar,并且要修改三个工厂类。这样大批量的改动是很丑陋的做法。所以可以用简单工厂配合反射来改进抽象工厂。


5、总结

(1)简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。 
(2)工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。 
(3)抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。

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


参考来源:

JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)

设计模式学习笔记-简单工厂模式

简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

java设计模式—工厂模式

工厂方法模式与抽象工厂模式



  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值