设计模式——2.1、创建型模式(简单工厂、工厂方法、抽象工厂)
在一切开始前,我们需要知道的是设计模式不是被凭空创造出来的,而是从实际编程经验中总结出来的。因此此篇博客只是谈谈我对创建型设计模式的理解,欢迎大家留言讨论,如有错误,也希望能够及时指正,不深感激。
一、简单工厂模式
定义:在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。(此模式并不属于GoF23个基本设计模式)
角色:Factory(工厂角色)、Product(抽象产品角色)、ConcreteProduct(具体产品类)
理解:简单工厂,正如其名称,很简单。这里,先来解释一下“简单”的含义。假设你是一个建筑项目的材料提供方,需要生产很多种不同类型的产品,现在你负责规划生产活动。那最简单的生产方式是什么呢?答案是盖一个厂房,然后把生产各种类型商品的生产线全放进去,需要什么产品就去给厂房发封电报,然后厂房就启动相应的生产线生产商品。
说明:但若采用这种生产方式,我们可以说很简单,但我们也可以说很混乱。所有的产品生产线堆在一个厂房里必然会导致问题。比如当需要新增一种商品的生产时,就需要把厂房打开,为生产线规划位置、进行配置等,这放在面向对象程序设计中就是不满足开闭原则的。
实例:
//角色:抽象产品
interface Shape
{
public void draw();
public void erase();
}
//角色:具体产品
class Circle implements Shape
{
public void draw()
{
System.out.println("绘制圆形");
}
public void erase()
{
System.out.println("删除圆形");
}
}
//角色:具体产品
class Rectangle implements Shape
{
public void draw()
{
System.out.println("绘制矩形");
}
public void erase()
{
System.out.println("删除矩形");
}
}
//角色:工厂
class ShapeFactory
{
public static Shape createShape(String type)
{
if(type.equalsIgnoreCase("circle"))
{
return new Circle();
}
else if(type.equalsIgnoreCase("rectangle"))
{
return new Rectangle();
}
else if(...)
.
.
.
}
}
可以发现,工厂需要根据外界传递进来的参数来返回对应的产品对象 ,这里的参数就是上面的电报消息,比如上面当传递进来的参数为‘circle’时就返回一个Circle类的对象。
二、工厂方法模式
定义:工厂方法模式也叫工厂模式。工厂模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成。
角色:Product(抽象产品)、ConcreteProduct(具体产品)、Factory(抽象工厂)、ConcreteFactory(具体工厂)
理解:在简单工厂中,所有产品的生产线是放在一个厂房里的,这样做虽然看似简单,实则管理困难,当需要添加新的产品生产线时就需要打开工厂对内部生产线进行添加,这是不符合面向对象设计原则(开闭原则)的。因此,工厂方法模式可以看做是对上面简单工厂的改进,这种模式所采用的逻辑是:为每一种产品的生产线单独建造一个厂房,这样就解决了上面遇到的问题。每当有新的产品生产线需要添加时,就直接为这条生产线盖一个厂房,需要某类产品时就找到对应的工厂进行生成即可。
这里的抽象工厂可以看做是厂房的建筑图纸,所有的具体厂房都根据这个图纸进行建造,站在外面看每个厂房都长得一样,但厂房的内部的生产线是不同的。这样可以理解抽象工厂和具体工厂的关系。抽象产品和具体产品的关系也可以以此类推。
说明:这种方法虽然使得程序后面的扩展变得方便,但当产品线越来越多时,就需要建造大量的具体工厂,这样一来会显得过于繁冗。
实例:
//角色:抽象产品(规定所有的产品都应该有显示自身信息的方法,即displayInfo)
class abstract Product{
public abstract void displayInfo();
}
//角色:具体产品_1
class ConcreteProduct_1{
public void displayInfo(){
System.out.println("这是一个ConcreteProduct_1类的产品");
}
}
//角色:具体产品_2
class ConcreteProduct_2{
public void displayInfo(){
System.out.println("这是一个ConcreteProduct_2");
}
}
//角色:抽象工厂
class abstract Factory{
public abstract Product produce();
}
//角色:具体工厂_1
class ConcreteFactory_1 extends Factory{
public Product produce(){
return new ConcreteProduct_1();
}
}
//角色:具体工厂_2
class ConcreteFactory_2 extends Factory{
public Product produce(){
return new ConcreteProduct_2();
}
}
可以发现,其中具体工厂_1只返回ConcreteProduct_1类产品,每一个具体工厂只返回一类特定的产品,换言之,每一类具体产品都有一个具体工厂负责生产它。实现了一个工厂只生产一类产品的设想。
具体的交互关系如下:
class Client {
public static viod main(String args[]){
Product product;//声明需要一个产品
Factory factory;//声明需要一个工厂来生产这个产品
factory = new ConcreteFactory_1();//现在确定这个工厂为ConcreteFactory_1类工厂
product = factory.produce();//这个产品为ConcreteFactory_1类共产生产出来的产品
product.displayInfo();//显示产品自身的信息,这里应为"这是一个ConcreteProduct_1类的产品"
}
}
三、抽象工厂模式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
角色:Product(抽象产品)、ConcreteProduct(具体产品)、Factory(抽象工厂)、ConcreteFactory(具体工厂)
理解:可以发现抽象工厂所包含的角色与工厂方法模式中的角色完全一致。其实这并不是巧合。抽象工厂模式是工厂方法模式的泛化版。换言之,工厂方法是一种特殊的抽象工厂模式。上面定义中提到的“提供一个…的接口”,这个接口指的就是抽象工厂。
前面的工厂方法中,每个厂房负责生产一种类型的产品,这是你作为一个材料提供方的安排。现在让我们观察的角度上升一个层次。在实际的生产过程中,提供材料的公司可能不止你一个。一个水管可能由A公司生产提供,也有可能由B公司生产提供。注意:这里说的是公司,而不是工厂。工厂按照之前的约定一类工厂只能生产一种产品,而一个公司却可以提供多种产品。
抽象工厂中每一家具体的工厂可以理解为一个公司,这个公司内部有多个部门负责提供不同的类型产品,同时,其它公司也能够生产相同类型的商品(这里的类型指的是抽象层的类型),比如说海尔公司可以生产空调、冰箱、洗衣机,同时格力公司也可以生产空调、冰箱、洗衣机。虽说生产的都是空调、冰箱、洗衣机,但海尔和格力具体生产出的产品又各有特色。因此这里空调、冰箱、洗衣机都是接口,即角色中的抽象产品,海尔和格力生产的相应产品必须先要实现对应的接口,即要拥有一个洗衣机应该有的基本功能,随后各自生产的洗衣机又可以有相应的特色,最终可以区分为海尔牌洗衣机和格力牌洗衣机两种类型。这里的类型则是指具体的产品类型,对应角色中的具体产品。
当用户确定了要购买洗衣机的时候,首先要确定买哪个公司的,公司确定了过后,该公司就会启动相应的部门生产洗衣机最终提供给用户。
说明:其实上面的东西听着特别像简单工厂,都是外界确定要什么类型的产品,然后启动工厂内部的生产线生产相应的产品,当需要添加新的产品种类时,就要更改工厂内部的代码,这也正是抽象工厂的缺点,当新的产品类型添加时,不符合开闭原则。而它的优点也有,它隔离了具体类的生产,从而使得一个具体工厂的创建变得相对容易,即只要实现抽象工厂的接口,就可以创建一个新的具体工厂。用户可以根据自己的需要配置自己需要的工厂。
实例:
//角色:抽象产品(确定产品的基本功能)
public interface TV{
public abstract void display();
}
//角色:抽象产品(确定产品的基本功能)
public interface AirConditioner{
public abstract void freeze();
}
//角色:具体产品
public class TCLTV implements TV{
public void display(){
System.out.println("正在播放TCL电视");
}
}
//角色:具体产品
public class TCLAirConditioner implements AirConditioner{
public void freeze(){
System.out.println("TCL空调正在制冷");
}
}
//角色:具体产品
public class HaierTV implements TV{
public void display(){
System.out.println("正在播放Haier电视");
}
}
//角色:具体产品
public class HaierAirConditioner implements AirConditioner{
public void freeze(){
System.out.println("Haier空调正在制冷");
}
}
//角色:抽象工厂(确定一个工厂中的基本行为)
public interface Factory{
public abstract TV produceTV();
public abstract AirConditioner produceAirConditioner();
}
//角色:具体工厂(Haier品牌的工厂)
public class Haier implements Factory{
public TV produceTV(){
return new HaierTV();
}
public AirConditioner produceAirConditioner(){
return new HaierAirConditioner();
}
}
//角色:具体工厂(TCL品牌的工厂)
public class TCL implements Factory{
public TV produceTV(){
return new TCLTV();
}
public AirConditioner produceAirConditioner(){
return new TCLAirConditioner();
}
}
具体的交互关系如下:
class Client{
public static viod main(String args[]){
TV tv; //用户需要买一台电视机
Factory factory;//需要一个工厂生产这台电视机
factory = new HaierFactory();//确定需要海尔工厂来进行生产
tv = factory.produceTV();//海尔工厂启动电视生产线生产电视
tv.display();//用户使用海尔电视进行播放
}
}
最终的结果应为“正在播放Haier电视”.