《设计模式》设计模式的基本原则
《设计模式》单例模式
《设计模式》工厂模式
《设计模式》原型模式
《设计模式》建造者模式
《设计模式》适配器模式
《设计模式》桥接模式
《设计模式》装饰者模式
《设计模式》组合模式
《设计模式》外观模式
《设计模式》享元模式
《设计模式》代理模式
《设计模式》模板方法模式
《设计模式》命令模式
有一个披萨的项目:
- 披萨的种类很多,有
GreekPizza
、CheesePizza
等 - 披萨的制作过程有
prepare
、bake
、cut
和box
- 完成披萨店的订购功能
使用传统的方式来完成,其 UML 图如下所示:
Pizza
类代码如下:
public abstract class Pizza {
protected String name; //名字
public abstract void prepare();
public void bake() {
System.out.println(name+"baking");
}
public void cut() {
System.out.println(name+"cutting");
}
public void box() {
System.out.println(name+"boxing");
}
public void setName(String name) {
this.name = name;
}
}
CheesePizza
类代码如下:
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("制作奶酪披萨,准备原材料");
}
}
GreekPizza
类代码如下:
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("制作希腊披萨,准备原材料");
}
}
PepperPizza
类代码如下:
public class PepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("给胡椒披萨准备原材料");
}
}
OrderPizza
类代码如下:
public class OrderPizza {
//构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; //订购披萨的类型
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
//输出pizza制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
}
1. 简单工厂模式
如果想要新增一个
Pizza
类,则会涉及到客户端OrderPizza
类,这种方式违背了设计模式的开闭原则,对修改关闭,对扩展开放。因此,可以考虑将创建 Pizza 对象封装到一个类中,这样有新的 Pizza 种类的时候,只需要修改该类即可,其他有创建到 Pizza 对象的代码就不需要修改,可以使用简单工厂模式。
定义:简单工厂模式属于创建型模式,是工厂模式的一种,它是由一个工厂对象决定创建出哪一种产品类的实例。定义一个创建对象的类,由这个类来封装实例化对象的行为。
使用场景:当用到大量的创建某种、某类或者某批对象的时候就会使用到工厂模式。
使用简单工厂模式重构代码,其 UML 图和实现代码如下所示:
简单工厂类 SimpleFactory
public class SimpleFactory {
//根据orderType返回对应的Pizza对象
public Pizza createPizza(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
//根据orderType返回对应的Pizza对象,简单工厂模式也叫静态工厂模式
public static Pizza createPizza2(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
订购披萨类 OrderPizza
public class OrderPizza {
Pizza pizza = null;
String orderType = "";
//构造器
public OrderPizza() {
do {
orderType = getType();
pizza = SimpleFactory.createPizza2(orderType);
if (pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购披萨失败");
break;
}
} while (true);
}
//获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
2. 工厂方法模式
此时披萨有一个新的需求,客户在点披萨时,可以点不同产地的披萨。例如,北京的奶酪披萨、北京的胡椒披萨或者上海的奶酪披萨等等。虽然依然使用简单工厂模式,创建不同的简单工厂类,例如 BJCheesePizza、BJPepperPizza,但是考虑到项目的规模以及软件的可维护性和可扩展性并不是特别好,这里考虑使用工厂方法模式。
- 将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
定义:定义一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类。
使用工厂方法模式思想设计的 UML 图如下所示:
Pizza
类
public abstract class Pizza {
protected String name; //名字
public abstract void prepare();
public void bake() {
System.out.println(name+"baking");
}
public void cut() {
System.out.println(name+"cutting");
}
public void box() {
System.out.println(name+"boxing");
}
public void setName(String name) {
this.name = name;
}
}
BJCheesePizza
类
public class BJCheesePizza extends Pizza{
@Override
public void prepare() {
setName("北京奶酪披萨");
System.out.println("北京的奶酪披萨,准备原材料");
}
}
BJPepperPizza
类
public class BJPepperPizza extends Pizza{
@Override
public void prepare() {
setName("北京的胡椒披萨");
System.out.println("北京的胡椒披萨,准备原材料");
}
}
SHCheesePizza
类
public class SHCheesePizza extends Pizza{
@Override
public void prepare() {
setName("上海的奶酪披萨");
System.out.println("上海的奶酪披萨,准备原材料");
}
}
SHPepperPizza
类
public class SHPepperPizza extends Pizza{
@Override
public void prepare() {
setName("上海的胡椒披萨");
System.out.println("上海的胡椒披萨,准备原材料");
}
}
OrderPizza
类
public abstract class OrderPizza {
//定义一个抽象方法createPizza,让各个工厂子类自己实现
abstract Pizza createPizza(String orderType);
//构造器
public OrderPizza() {
Pizza pizza = null;
String orderType = "";
do {
orderType = getType();
pizza = createPizza(orderType);
//制作过程
if (pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购披萨失败");
break;
}
} while (true);
}
//获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
BJOrderPizza
类
public class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
SHOrderPizza
类
public class SHOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new SHCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new SHPepperPizza();
}
return pizza;
}
}
3. 抽象工厂模式
定义:
- 定义了一个接口用于创建相关或有依赖关系的对象簇,而无需指明具体的类
- 将工厂抽象成两层:AbsFactory(抽象工厂)和具体实现的工厂子类。开发人员根据创建对象类型使用对应的工厂子类,这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
- 从设计层面看,抽象工厂模式就是对简单工厂模式的进一步抽象。
使用抽象工厂模式重构披萨项目,其 UML 图如下所示:
AbsFactory
接口
public interface AbsFactory {
//让下面的工厂子类具体实现
Pizza createPizza(String orderType);
}
BJFactory
类
public class BJFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
System.out.println("使用的是抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
SHFactory
类
public class SHFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
System.out.println("使用的是抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new SHCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new SHPepperPizza();
}
return pizza;
}
}
OrderPizza
类
public abstract class OrderPizza {
AbsFactory absFactory;
//构造器
public OrderPizza(AbsFactory absFactory) {
Pizza pizza = null;
String orderType = "";
this.absFactory = absFactory;
do {
orderType = getType();
pizza = absFactory.createPizza(orderType);
//制作过程
if (pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购披萨失败");
break;
}
} while (true);
}
//获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
4. 工厂模式在 JDK 中的应用
JDK 中的 Calendar
类中就使用了简单工厂模式,Calendar 类中的静态方法 createCalendar
部分代码代码如下:
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale); //extends GregorianCalendar
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale); //extends Calendar
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale); //extends Calendar
break;
}
}
}
从以上代码可以看到,其实 Calendar
相当于一个简单工厂,根据不同的caltype
来创建对应的 Calendar
对象。
5. 小结
工厂模式的意义:
- 将实例化的代码提取处理出来,放到一个类中进行统一的管理和维护,达到与主项目解耦,提高系统可可扩展性和可维护性。
- 三种模式:简单工厂模式、工厂方法模式、抽象工厂模式。
工厂模式的依赖抽象原则:
- 创建对象实例时,不要直接使用直接创建对象,而是将创建对象的工作放在一个工厂的方法中再返回。
- 不要让类继承具体的类,而是继承抽象类或接口。
- 不要覆盖基类中已经实现的方法。