目录
一、创建型模式
一句话概括
1.1、工厂模式
1.1.1、简单工厂模式(非 23 种经典设计模式)
概述
案例
1.1.2、静态工厂(扩展)
1.1.3、工厂方法模式
概念
案例
1.2、建造者模式
1.2.1、概念
1.2.2、案例
1.2.3、建造者模式扩展:链式编程底层
1.3、工厂方法模式 VS 建造者模式
一、创建型模式
一句话概括
创建型模式:教你如果创建对象.
1.1、工厂模式
1.1.1、简单工厂模式(非 23 种经典设计模式)
概述
简单工厂不是一种设计模式,反而比较像是一种编程习惯.
简单工厂包含一下角色:
- 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品 :实现或者继承抽象产品的子类
- 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品
案例
需求:设计一个咖啡店点餐系统.
思路:设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡 【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。
以下代码为空架.
/**
* 咖啡
*/
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addWeight() {
System.out.println("加量");
}
}
/**
* 美式咖啡
*/
public class AmericanCoffee extends Coffee{
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* 拿铁咖啡
*/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
/**
* 咖啡店
*/
public class CoffeeStore {
public Coffee orderCoffee(String type) {
//1.根据订单类型选取对应的咖啡
Coffee coffee = null;
if (type == null || type.equals("")) {
throw new RuntimeException("type 非法");
} else if (type.equals("ac")) {
coffee = new AmericanCoffee();
} else if(type.equals("lc")) {
coffee = new LatteCoffee();
} else {
throw new RuntimeException("type 非法");
}
//2.加糖加量
coffee.addSugar();
coffee.addWeight();
return coffee;
}
}
/**
* 测试客户端
*/
public class ClientCoffee {
public static void main(String[] args) {
CoffeeStore store = new CoffeeStore();
Coffee coffee = store.orderCoffee("ac");
System.out.println(coffee.getName());
}
}
可以产出上述代码中 Coffee 类为抽象产品,AmericanCoffee 和 LatteCoffee 为具体产品,但是没有抽象工厂. 以下使用 简单工厂模式 对上述代码进行改造.
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addWeight() {
System.out.println("加量");
}
}
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
/**
* 简单工厂模式
*/
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
//1.根据订单类型选取对应的咖啡
Coffee coffee = null;
if (type == null || type.equals("")) {
throw new RuntimeException("type 非法");
} else if (type.equals("ac")) {
coffee = new AmericanCoffee();
} else if(type.equals("lc")) {
coffee = new LatteCoffee();
} else {
throw new RuntimeException("type 非法");
}
//2.加糖加量
coffee.addSugar();
coffee.addWeight();
return coffee;
}
}
public class CoffeeStore {
public Coffee orderCoffee(String type) {
//1.让工厂来创建对象
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
Coffee coffee = factory.createCoffee(type);
//2.加料
coffee.addWeight();
coffee.addSugar();
return coffee;
}
}
public class ClientCoffee {
public static void main(String[] args) {
CoffeeStore store = new CoffeeStore();
Coffee coffee = store.orderCoffee("ac");
System.out.println(coffee.getName());
}
}
上述代码中有了 SimpleCoffeeFactory ,使得 Coffee 的具体实现类和 CoffeeStore 解耦合,但是又产生了 CoffeeStore 和 SimpleCoffeeFactory 对象的耦合. 后期我们如果要加新的咖啡品种,势必需要修改 SimpleCoffeeFactory 的代码,违反了开闭原则.
优点:
解耦合:将对象的创建和业务逻辑层分开,避免将来修改客户端代码. 入股偶要实现新产品,直接修改工厂类,不需要再原客户端代码,更容易扩展.
缺点:
违背“开闭原则”:增加新产品时还需要修改工厂类代码.
1.1.2、静态工厂(扩展)
在实际的开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,避免对象的重复创建. 他也不是属于 23 中设计模式中的.
/**
* 简单工厂模式
*/
public class StaticCoffeeFactory {
public static Coffee createCoffee(String type) {
//1.根据订单类型选取对应的咖啡
Coffee coffee = null;
if (type == null || type.equals("")) {
throw new RuntimeException("type 非法");
} else if (type.equals("ac")) {
coffee = new AmericanCoffee();
} else if(type.equals("lc")) {
coffee = new LatteCoffee();
} else {
throw new RuntimeException("type 非法");
}
//2.加糖加量
coffee.addSugar();
coffee.addWeight();
return coffee;
}
}
1.1.3、工厂方法模式
概念
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式包含以下角色:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂 方法来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同 具体工厂之间一一对应。
案例
需求:设计一个咖啡店点餐系统,咖啡种类有美式咖啡和拿铁咖啡.
/**
* 抽象产品: 咖啡类
*/
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addWeight() {
System.out.println("加量");
}
}
/**
* 具体产品:美式咖啡类
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* 具体产品:拿铁咖啡类
*/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
/**
* 抽象工厂: 咖啡工厂
*/
public interface CoffeeFactory {
Coffee createCoffee();
}
/**
* 具体工厂:美式咖啡工厂
*/
public class AmericanCoffeeFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
/**
* 具体工厂:拿铁咖啡工厂
*/
public class LatteCoffeeFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
/**
* 咖啡商店
*/
public class CoffeeStore {
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee() {
//1.创建咖啡对象
Coffee coffee = factory.createCoffee();
//2.加料
coffee.addWeight();
coffee.addSugar();
return coffee;
}
}
public class ClientCoffee {
public static void main(String[] args) {
CoffeeStore store = new CoffeeStore();
// AmericanCoffeeFactory factory = new AmericanCoffeeFactory();
LatteCoffeeFactory factory = new LatteCoffeeFactory();
store.setFactory(factory);
Coffee coffee = store.orderCoffee();
System.out.println(coffee.getName());
}
}
优点:
使用便利:用户只需要知道具体的工厂名称就可以拿到产品,无须知道产品的具体创建过程.
满足“开闭原则”:当需要添加新的产品时,无需对原工厂进行修改,只需要添加具体的产品类和工厂即可.
缺点:
“类爆炸”:每增加一个产品就需要增加一个具体产品类和一个对应的具体工厂类,增加系统复杂度.
使用场景:
- 重复代码 : 创建对象需要使用大量重复的代码 ;
- 不关心创建过程 : 客户端 不依赖 产品类 , 不关心 实例 如何被创建 , 实现等细节 ;
- 创建对象 : 一个类通过其子类来指定创建哪个对象 ;
1.2、建造者模式
1.2.1、概念
将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。例如电脑的主机由 cpu、内存、主板、显卡 构成,用户只需要指定主机的类型就可以得到相应的主机,无需知道内部的具体构造细节.
建造者模式包含如下角色:
- 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
- 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体 创建方法。在构造过程完成后,提供产品的实例。
- 产品类(Product):要创建的复杂对象。
- 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产 品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
1.2.2、案例
生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质 的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。
/**
* 产品类:自行车类
*/
public class Bike {
//车架
private String frame;
//车座
private String seat;
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
/**
* 抽象建造者类
*/
public abstract class Builder {
//这里只是一个空架子,还需要具体实现
protected Bike bike = new Bike();
//自行车的组成部分
public abstract void buildFrame();
public abstract void buildSeat();
//构建自行车
public abstract Bike createBike();
}
/**
* 具体建造者类:捷安特建造者类
*/
public class GiantBuilder extends Builder{
@Override
public void buildFrame() {
this.bike.setFrame("碳纤维车架");
}
@Override
public void buildSeat() {
this.bike.setSeat("橡胶车座");
}
@Override
public Bike createBike() {
return this.bike;
}
}
/**
* 具体建造者类:摩拜建造者类
*/
public class MobikeBuilder extends Builder {
@Override
public void buildFrame() {
this.bike.setFrame("铝合金车架");
}
@Override
public void buildSeat() {
this.bike.setSeat("真皮车座");
}
@Override
public Bike createBike() {
return this.bike;
}
}
/**
* 指挥者类
*/
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
/**
* 指导构造
* @return
*/
public Bike construct() {
//开始构建
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
//建造捷安特自行车
showBike(new GiantBuilder());
//建造摩拜自行车
showBike(new MobikeBuilder());
}
private static void showBike(Builder builder) {
Director director = new Director(builder);
Bike bike = director.construct();
System.out.println(bike.getFrame());
System.out.println(bike.getSeat());
}
}
优点:
解耦合:客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得 相同的创建过程可以创建不同的产品对象。
易扩展,符合开闭原则:如果有新的需求,通过实现一个新的建造者就可以完成,基本上不用修改其他代码.
缺点:
如果产品之间的差异性比较大,则不适合使用,因此使用范围受到一定的限制.
使用场景:
创建对象的过程比较负责,由多个部件构成,但构建的顺序是稳定的.
产品的构建过程和最终的表示是独立的.
1.2.3、建造者模式扩展:链式编程底层
建造者模式除了上述用途以外,在开发中还有一种常用的使用方式,就是当一个类构造器需要传入多个参数,此时创建这个类的实例,代码的可读性就会非常差,而且很容易引入新的错误. 此时就可以使用 链式编程底层 的方式对 建造者模式进行重构.
重构后代码如下:
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainBoard;
//私有构造,只对内提供
private Phone(Builder builder) {
this.cpu = builder.cpu;
this.screen = builder.screen;
this.memory = builder.memory;
this.mainBoard = builder.mainBoard;
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainBoard='" + mainBoard + '\'' +
'}';
}
//链式编程构建
public static class Builder {
private String cpu;
private String screen;
private String memory;
private String mainBoard;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder screen(String screen) {
this.screen = screen;
return this;
}
public Builder memory(String memory) {
this.memory = memory;
return this;
}
public Builder mainBoard(String mainBoard) {
this.mainBoard = mainBoard;
return this;
}
public Phone build() {
return new Phone(this);
}
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.cpu("intel")
.screen("三星屏幕")
.mainBoard("华硕")
.memory("金士顿")
.build();
System.out.println(phone);
}
}
重构后使用起来更加方便,提高开发效率. 但是从软件设计上,对程序员的要求比较高.
1.3、工厂方法模式 VS 建造者模式
工厂方法模式注重是整体对象的创建方式;而建造者模式注重的是部件一步一步的创建出一个复杂对象的过程 .
比如我要制造一个超人,如果使用工厂方法模式,直接产生出来就是一个力大无穷、能飞,内裤外穿的超人;而如果使用建造者模式,则需要组装 手、头、脚、躯干、裤头... 一个超人就诞生了.