创造对象的艺术——深入理解工厂模式

1. 简单工厂模式(静态工厂方法模式)

简单工厂模式其实并不算是一种设计模式,更多的是一种编程习惯。

1.1 定义

定义一个工厂类,根据传入的参数的不同返回不同的实例,被创建的实例具有共同的接口或者父类;因为简单工厂模式中创建实例的方法是静态方法,因此又称为静态工厂模式

1.2 结构

  • Factory工厂角色负责实现创建所有具体产品的内部逻辑,工厂类中提供了静态的工厂方法,该方法可以根据传入的参数创建不同的具体产品对象,可以直接被外部调用;

  • Product抽象产品角色是所创建的所有对象的父类,是所有具体产品角色的公共代码集合;

  • ContreteProduct具体产品角色是工厂类创建的目标,是抽象产品的具体,也就是需要创建的具体对象;

1.3 示例

创建一个水果工厂,水果工厂中可以创建苹果、香蕉等各种各样的水果。

抽象产品类:

public abstract class Fruits {
     public abstract void produce();
 }

具体产品角色:

 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("生产了一个梨");
     }
 }

工厂类:

 // 工厂类提供静态的方法
 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();
         }
     }
 }

客户端类:

 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 示例

苹果工厂专门用来创建苹果,香蕉工厂专门用来创建香蕉等,这些工厂都继承自同一个基类。

抽象工厂类:

 public abstract class Factory {
     public abstract Fruits product();
 }

具体工厂:

 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();
     }
 }

抽象产品:

public abstract class Fruits {
     public abstract void show();
 }

具体产品:

 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)

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 示例

北方的人喜欢吃北方产的水果,而南方的人喜欢吃南方产的水果,所以工厂就在北方建了一个工厂,负责生产北方的水果,在南方也建了一个工厂,负责生产南方的水果

抽象工厂类:

public abstract class FruitFactory {
    public abstract Apple productApple();
    public abstract Banana productBanana();
    public abstract Orange productOrange();
}

具体工厂类:

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();
    }
}

抽象产品类:

public abstract class Apple {
    public abstract void get();
}
public abstract class Banana {
    public abstract void get();
}
public abstract class Orange {
    public abstract void get();
}

具体产品类:

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("得到一个南方橘子");
    }
}

客户端:

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 适用场景

  • 系统中可以划分出多于一个的产品族,而且每次只使用其中的某一个;

  • 产品等级结构比较稳定;

3.6 UML图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值