创造对象的艺术------深入理解工厂模式
1. 简单工厂模式(静态工厂方法模式)
简单工厂模式
其实并不算是一种设计模式,更多的是一种编程习惯。
1.1 定义
定义一个工厂类,根据传入的参数的不同返回不同的实例,被创建的实例具有共同的接口或者父类;因为简单工厂模式中创建实例的方法是静态方法,因此又称为静态工厂模式。
1.2 结构
- Factory :
工厂角色
负责实现创建所有具体产品
的内部逻辑,工厂类中提供了静态的
工厂方法,该方法可以根据传入的参数创建不同的具体产品对象,可以直接被外部调用; - Product :
抽象产品角色
是所创建的所有对象的父类,是所有具体产品
角色的公共代码集合; - ContreteProduct :
具体产品角色
是工厂类创建的目标,是抽象产品
的具体,也就是需要创建的具体对象;
1.3 示例
创建一个水果工厂,水果工厂中可以创建苹果、香蕉等各种各样的水果。
抽象产品类:
csharp
public abstract class Fruits {
public abstract void produce();
}
具体产品角色:
scala
public class Apple extends Fruits{
@Override
public void produce() {
System.out.println("生产了一个Apple");
}
}
public class Banana extends Fruits{
@Override
public void produce() {
System.out.println("生产了一个香蕉");
}
}
public class Pear extends Fruits{
@Override
public void produce() {
System.out.println("生产了一个梨");
}
}
工厂类:
csharp
// 工厂类提供静态的方法
public class FruitsFactory {
public static Fruits getFruit(String type) throws Exception {
if ("apple".equals(type)) {
return new Apple();
} else if ("pear".equals(type)) {
return new Pear();
} else if ("banana".equals(type)) {
return new Banana();
} else {
System.out.println("输入的苹果类型有误~!");
throw new Exception();
}
}
}
客户端类:
ini
public class Client {
public static void main(String[] args) throws Exception {
Fruits apple = FruitsFactory.getFruit("apple");
Fruits banana = FruitsFactory.getFruit("banana");
Fruits pear = FruitsFactory.getFruit("pear");
apple.produce();
banana.produce();
pear.produce();
}
}
1.4 优缺点
1.4.1 优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以不需要创建具体的对象,仅仅使用具体的产品即可;(提供了一个工厂类,专门创建对象)
- 可以通过引入配置文件的方式,可以不修改任何客户端代码来更换和新增产品类,提高了系统的灵活性;
- 客户端无须知道创建具体产品类的类名,指需要知道具体产品对应的参数即可。
1.4.2 缺点
- 由于工厂类集中了所有的创建逻辑,一旦不能正常工作,整个系统将受到影响;(职责过重)
- 简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解的难度;
- 当需要添加新的产品时,需要修改工厂的逻辑;在产品类型较多时,工厂的逻辑变的复杂,不利于扩展和维护(违反了开闭原则)
1.5 适用场景
- 工厂类创建的对象较少时(工厂的逻辑不会过于复杂)
- 客户端只需要传入工厂类需要的参数,不关注创建过程
1.6 UML图
2. 工厂方法模式(工厂模式、虚拟构造器模式、多态工厂模式)
简单工厂
很简单,但是我们发现如果需要添加新的产品时,我们必定会修改工厂类的源代码,违反了开闭原则。所以我们引入了工厂方法模式
,针对不同的对象提供不同的工厂。
2.1 定义
定义一个抽象工厂类,工厂类中定义了抽象工厂方法,该方法定义了创建对象的公共接口,具体的创建细节由子类来实现。(将产品的实例化操作延迟到工厂的子类中完成)。
2.2 结构
- Product :
抽象产品
是对产品进行声明; - ConcreteProduct :
具体产品
是抽象产品的具体实现; - Factory :
抽象工厂
声明一个创建产品的工厂方法,该方法的返回值是产品基类; - ConcretFactory :
具体工厂
重写抽象工厂中创建的工厂方法,使其返回不同类型的产品;
2.3 示例
苹果工厂专门用来创建苹果,香蕉工厂专门用来创建香蕉等,这些工厂都继承自同一个基类。
抽象工厂类:
csharp
public abstract class Factory {
public abstract Fruits product();
}
具体工厂:
scala
public class AppleFactory extends Factory{
@Override
public Fruits product() {
return new Apple();
}
}
public class BananaFactory extends Factory{
@Override
public Fruits product() {
return new Banana();
}
}
public class PearFactory extends Factory{
@Override
public Fruits product() {
return new Pear();
}
}
抽象产品:
csharp
public abstract class Fruits {
public abstract void show();
}
具体产品:
scala
public class Apple extends Fruits {
@Override
public void show() {
System.out.println("一个甜苹果");
}
}
public class Banana extends Fruits {
@Override
public void show() {
System.out.println("一个大香蕉");
}
}
public class Pear extends Fruits {
@Override
public void show() {
System.out.println("一个红色的桃子");
}
}
客户端(Client)
ini
public class Client {
public static void main(String[] args) {
// 需要生产苹果
Factory appleFactory = new AppleFactory();
Fruits apple = appleFactory.product();
apple.show();
// 需要生产香蕉
Factory bananaFactory = new BananaFactory();
Fruits banana = bananaFactory.product();
banana.show();
// 需要生产桃子
Factory pearFactory = new PearFactory();
Fruits pear = pearFactory.product();
pear.show();
}
}
2.4 优缺点
2.4.1 优点
- 工厂方法被用来创建客户所需要的产品,同时向客户端隐藏具体的产品被创建的过程;(避免创建者和具体产品的耦合)
- 每个工厂只负责创建一个产品;(单一职责)
- 添加新的产品时,只需要添加一个具体的产品和具体的产品工厂,扩展性变的非常好;(开放封闭原则)
2.4.2 缺点
- 添加新的产品时,需要添加产品类和工厂类,系统类的数量成对增加,增加了系统复杂度,同时因为需要编译和运行,给系统带来额外的开销;
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度;
2.5 适用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展;
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中;
2.6 UML图
3. 抽象工厂模式
工厂方法模式
中的每一个工厂都只生产一类产品,当产品过多时,会导致系统重存在大量的工厂类,我们可以将一些拥有相关特性的产品组成一个产品族
,由同一个工厂生产这个产品族,这就是抽象工厂模式
为了更好的理解抽象工厂模式,我们先了解两个概念:
- 产品的等级结构:产品的等级结构就是产品的继承结构。 比如:一个抽象类是电视机,子类有海尔电视机、海信电视机、TCL电视机,抽象类和具体的子类就构成了产品等级机构。
- 产品族:由同一个工厂产生的不同的产品类型。 比如:海尔工厂生产电视机、电冰箱构成了一个产品族。
3.1 定义
在抽象工厂中声明多个工厂方法,具体工厂实现了抽象工厂中定义的工厂方法生成具体的产品,这种模式称为抽象工厂模式
。
3.2 结构
- AbstractFactory:
抽象工厂
声明了一组用于创建一族产品的方法,每个方法对应一个产品; - ConcreteFactory:
具体工厂
实现了在抽象工厂中定义的方法,用于生成每一个具体的产品; - AbstractProduct:
抽象产品
定义每一种产品所具体的公共逻辑; - ConcreteProduct:
具体产品
定义具体工厂生产的具体产品;
3.3 示例
北方的人喜欢吃北方产的水果,而南方的人喜欢吃南方产的水果,所以工厂就在北方建了一个工厂,负责生产北方的水果,在南方也建了一个工厂,负责生产南方的水果
抽象工厂类:
csharp
public abstract class FruitFactory {
public abstract Apple productApple();
public abstract Banana productBanana();
public abstract Orange productOrange();
}
具体工厂类:
scala
public class NorthFruitFactory extends FruitFactory{
@Override
public Apple productApple() {
return new NorthApple();
}
@Override
public Banana productBanana() {
return new NorthBanana();
}
@Override
public Orange productOrange() {
return new NorthOrange();
}
}
public class SouthFruitFactory extends FruitFactory{
@Override
public Apple productApple() {
return new SouthApple();
}
@Override
public Banana productBanana() {
return new SouthBanana();
}
@Override
public Orange productOrange() {
return new SouthOrange();
}
}
抽象产品类:
csharp
public abstract class Apple {
public abstract void get();
}
public abstract class Banana {
public abstract void get();
}
public abstract class Orange {
public abstract void get();
}
具体产品类:
scala
public class NorthApple extends Apple{
@Override
public void get() {
System.out.println("得到一个北方苹果");
}
}
public class NorthBanana extends Banana{
@Override
public void get() {
System.out.println("得到一个北方香蕉");
}
}
public class NorthOrange extends Orange {
@Override
public void get() {
System.out.println("得到一个北方橘子");
}
}
public class SouthApple extends Apple{
@Override
public void get() {
System.out.println("得到一个南方苹果");
}
}
public class SouthBanana extends Banana{
@Override
public void get() {
System.out.println("得到一个南方香蕉");
}
}
public class SouthOrange extends Orange {
@Override
public void get() {
System.out.println("得到一个南方橘子");
}
}
客户端:
ini
public class Client {
public static void main(String[] args) {
FruitFactory northFruitFactory = new NorthFruitFactory();
Apple northApple = northFruitFactory.productApple();
northApple.get();
Banana northBanana = northFruitFactory.productBanana();
northBanana.get();
Orange northOrange = northFruitFactory.productOrange();
northOrange.get();
FruitFactory southFruitFactory = new SouthFruitFactory();
Apple southApple = southFruitFactory.productApple();
southApple.get();
Banana southBanana = southFruitFactory.productBanana();
southBanana.get();
Orange southOrange = southFruitFactory.productOrange();
southOrange.get();
}
}
3.4 优缺点
3.4.1 优点
- 新增新的产品族时,无需修改已有代码(符合开闭原则)
- 产品生成隔离开来,易于代码维护(符合单一职责)
3.4.2 缺点
- 新增新的产品等级结构时比较负责,需要对原有结构进行较大修改(违背开闭原则)
3.5 适用场景
- 系统中可以划分出多于一个的产品族,而且每次只使用其中的某一个;
- 产品等级结构比较稳定;