设计模式介绍
设计模式是程序员在面对同类软件工程设计问题所总结出来的有用经验,是某类问题的通用解决方案。
作用:使程序(软件)具有更好:
- 代码重用性(即相同功能的代码,不用多次编写)
- 可读性(即编程规范性,便于其他程序员的阅读和理解)
- 可扩展性(即:当需要增加新的功能时,非常的方便,称为可维护)
- 可靠性(即当增加新的功能时,对原先的功能没有影响)
- 使程序呈现高内聚、低耦合的特性。
七大设计原则
设计模式原则就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)。
设计模式常用的七大原则有:
单一职责原则 | 一个类应该只负责一项职责 |
---|---|
接口隔离原则 | 一个类对另一个类的依赖应该建立在最小接口上 |
依赖倒转(倒置)原则 | 高层模块不应该依赖底层模块,二者都应该依赖抽象。 |
里氏替换原则 | 所有引用基类的地方必须能透明地使用其子类的对象 |
开闭原则 | 一个软件实体如类、模块和函数应该对扩展(提供方)开放,对修改(使用方)关闭 |
迪米特原则 | 一个类对自己依赖的类知道的越少越好 |
合成复用原则 | 尽量使用合成/聚合,而不是通过继承达到复用的目的。 |
单一职责原则
一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更
而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2
作用:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性。
- 降低变更引起的风险。
接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
应用实例
先看一张图:
上图中出现的问题:类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。
按隔离原则应当这样处理:将接口Interface1拆分为独立的几个接口(这里我们拆分成3个接口),类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。如下类图
依赖倒转原则
依赖倒转原则(Dependence Inversion Principle)是指:
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架
构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类 - 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
应用实例
方案1+分析说明
package com.atguigu.principle.inversion;
public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email {
public String getInfo() {
return "电子邮件信息: hello,world";
}
}
//完成Person接收消息的功能
//方式1分析
//1. 简单,比较容易想到
//2. 如果我们获取的对象是 微信,短信等等,则新增类,同时Perons也要增加相应的接收方法
//3. 解决思路:引入一个抽象的接口IReceiver, 表示接收者, 这样Person类与接口IReceiver发生依赖
// 因为Email, WeiXin 等等属于接收的范围,他们各自实现IReceiver 接口就ok, 这样我们就符号依赖倒转原则
class Person {
public void receive(Email email ) {
System.out.println(email.getInfo());
}
}
方案2(依赖倒转) +分析说明
package com.atguigu.principle.inversion.improve;
public class DependecyInversion {
public static void main(String[] args) {
//客户端无需改变
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
//定义接口
interface IReceiver {
public String getInfo();
}
class Email implements IReceiver {
public String getInfo() {
return "电子邮件信息: hello,world";
}
}
//增加微信
class WeiXin implements IReceiver {
public String getInfo() {
return "微信信息: hello,ok";
}
}
//方式2
class Person {
//这里我们是对接口的依赖
public void receive(IReceiver receiver ) {
System.out.println(receiver.getInfo());
}
}
里氏替换原则
OO中的继承性的思考和说明
- 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有
的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。 - 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
- 问题提出:在编程中,如何正确的使用继承? =>里氏替换原则
基本介绍
- 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的以为姓里的女士提出的。
- 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
应用实例
package com.atguigu.principle.liskov;
public class Liskov {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B b = new B();
System.out.println("11-3=" + b.func1(11, 3));//这里本意是求出11-3
System.out.println("1-8=" + b.func1(1, 8));// 1-8
System.out.println("11+3+9=" + b.func2(11, 3));
}
}
// A类
class A {
// 返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B类继承了A
// 增加了一个新功能:完成两个数相加,然后和9求和
class B extends A {
//这里,重写了A类的方法, 可能是无意识
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
}
问题:我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
解决办法:通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。
改进方案:
package com.atguigu.principle.liskov.improve;
public class Liskov {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B b = new B();
//因为B类不再继承A类,因此调用者,不会再func1是求减法
//调用完成的功能就会很明确
System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出11+3
System.out.println("1+8=" + b.func1(1, 8));// 1+8
System.out.println("11+3+9=" + b.func2(11, 3));
//使用组合仍然可以使用到A类相关方法
System.out.println("11-3=" + b.func3(11, 3));// 这里本意是求出11-3
}
}
//创建一个更加基础的基类
class Base {
//把更加基础的方法和成员写到Base类
}
// A类
class A extends Base {
// 返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B类继承了A
// 增加了一个新功能:完成两个数相加,然后和9求和
class B extends Base {
//如果B需要使用A类的方法,使用组合关系
private A a = new A();
//这里,重写了A类的方法, 可能是无意识
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
//我们仍然想使用A的方法
public int func3(int a, int b) {
return this.a.func1(a, b);
}
}
开闭原则
一个软件实体如类、模块和函数应该对扩展(提供方)开放,对修改(使用方)关闭。
迪米特原则
一个对象应该对其他对象保持最少的了解。又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。
尽量降低类与类的耦合。强调只和直接朋友(成员变量、方法参数、方法返回值)交流,不和陌生人(局部变量中的类)说话。
迪米特的特性是降低类的耦合,并不要求完全没有依赖关系。
合成复用原则
定义:尽量使用合成/聚合,而不是通过继承达到复用的目的。
设计模式分类
设计模式分为三大类,总共有23种模式。具体分类:
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂方法模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)
23种设计模式详解
单例模式
单例模式:保证一个类只有一个实例,并提供唯一获取这个实例的入口。
单例模式总的有八种:
- 饿汉式(静态变量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全)
- 懒汉式(线程安全,同步代码块)
- 双重检测(DoubleCheck)
- 静态内部类
- 枚举
饿汉式(静态变量)
具体做法:
- 构造器私有化,防止外部创建对象
- 声明一个静态成员变量并创建相应的对象实例
- 对外提供一个静态公用方法获取这个对象
优点:线程安全,在类加载时就已经创建实例
缺点:如果创建的对象实例没有被用到,就会造成内存浪费。
结论:当保证对象会被使用到,可以使用这种方法。
代码样例:
package signleton.type1;
public class SignletonTpye1 {
//1.构造器私有化,防止外部创建对象
//2.声明一个静态成员变量并创建相应的对象实例
//3.对外提供一个静态公用方法获取这个对象
private SignletonTpye1() {
}
private static final SignletonTpye1 instance = new SignletonTpye1();
public static SignletonTpye1 getInstance() {
return instance;
}
}
饿汉式(静态代码块)
效果跟饿汉式静态常量一致,只不过创建的对象改在静态代码块创建。
代码样例:
package signleton.type1;
public class SignLetonType2 {
private SignLetonType2() {
}
private static SignLetonType2 instance;
static {
instance = new SignLetonType2();
}
public static SignLetonType2 getInstance() {
return instance;
}
}
懒汉式(线程不安全)
优点:懒加载,不会浪费内存资源。
缺点:线程不安全
结论:不推荐使用
代码样例:
package signleton.type1;
public class SignLetonType3 {
private SignLetonType3() {
}
private static SignLetonType3 instance;
public static SignLetonType3 getInstance() {
if(instance == null) {
instance = new SignLetonType3();
}
return instance;
}
}
懒汉式(线程安全)
优点:懒加载,线程安全
缺点:并发度低,可能会导致性能瓶颈
结论:不推荐使用
代码样例:
package signleton.type1;
public class SignletonType4 {
private SignletonType4() {
}
private static SignletonType4 instance;
public static synchronized SignletonType4 getInstance() {
if(instance == null) {
instance = new SignletonType4();
}
return instance;
}
}
懒汉式(线程安全,同步代码块)
优点:懒加载
缺点:线程不安全
结论:不推荐使用
代码样例:
public class SignletonType5 {
private SignletonType5() {
}
private static SignletonType5 instance;
public static SignletonType5 getInstance() {
if(instance == null) {
synchronized (SignletonType5.class) {
instance = new SignletonType5();
}
}
return instance;
}
}
双重检测(DoubleCheck)
即支持懒加载,又支持高并发,推荐使用。
代码样例:
package signleton.type1;
public class SignletonType6 {
private SignletonType6() {
}
private static volatile SignletonType6 instance;
public static SignletonType6 getInstance() {
if(instance == null) {
synchronized (SignletonType6.class) {
if(instance == null) {
instance = new SignletonType6();
}
}
}
return instance;
}
}
volatile的作用是禁止指令重排。instance = new SignletonType6()不是一个原子操作,在JVM至少分为三步:
- 给instance分配内存空间。
- 调用SignletonType6的构造函数实例化instance
- 将instance对象指向分配的内部空间(instance 就不是null)
2,3步的顺序不一定,可能顺序是1,3,2。那么这时使用instance这个实例对象就会有问题。
静态内部类
优点:线程安全,懒加载
结论:推荐使用
代码样例:
package signleton.type1;
public class SignletonType7 {
private SignletonType7() {
}
private static class SignletonIntance {
private static final SignletonType7 instance = new SignletonType7();
}
public static SignletonType7 getInstance() {
return SignletonIntance.instance;
}
}
利用了静态内部类的特点,当用到静态内部类时才会进行类加载,类加载时线程安全的。
枚举
使用枚举的特性保证实例的线程安全和唯一性。
代码样例:
package signleton.type1;
public enum SignletonType8 {
instance;
public void say() {
System.out.println("to say...");
}
}
JDK使用的单例模式
java.lang.Runtime使用的饿汉式(静态变量)单例模式。
工厂模式
工厂模式的核心思想就是将实例化的代码抽离出来,交由一个类统一管理和维护,进行解耦,提高可扩展性和维护性。
背景引入
场景:采购披萨
- 有CheesePizza(奶酪披萨)、GreekPizza(希腊披萨)
- prepare(准备披萨)、bake(烘烤披萨)、cut(切披萨)、box(打包披萨)
原始实现方式
相关代码:
- Pizza抽象类
package factory.simplefatory.pizza;
public abstract class Pizza {
public abstract void prepare();
public void bake() {
System.out.println("烘烤披萨");
}
public void cut() {
System.out.println("切披萨");
}
public void box() {
System.out.println("打包披萨");
}
}
- CheesePizza
package factory.simplefatory.pizza;
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备奶酪披萨原材料");
}
}
- GreekPizza
package factory.simplefatory.pizza;
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备希腊披萨原材料");
}
}
- OrderPizza(披萨下订单类)
package factory.simplefatory.order;
import factory.simplefatory.pizza.CheesePizza;
import factory.simplefatory.pizza.GreekPizza;
import factory.simplefatory.pizza.Pizza;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class OrderPizza {
OrderPizza() {
Pizza pizza = null;
while (true) {
String orderType = getOrderType();
if("cheese".equals(orderType)) {
pizza = new CheesePizza();
} else if("greek".equals(orderType)) {
pizza = new GreekPizza();
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
private String getOrderType() {
InputStream in = System.in;
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("请输入订购披萨类型");
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
类图如下:
问题:不利于扩展。如果现在新增一类披萨的话,使用者OrderPizza也要相应改变,OrderPizza有多个的话,影响的范围就太大了。
解决方法:OrderPizza不再创建披萨实例,将披萨实例的创建交由一个工厂类处理,后续如果有改动只需要改这个工厂就可以了。这就是简单工厂模式。
简单工厂模式
简单工厂模式就是将子类对象的创建交由一个工厂类进行创建,不再让使用方进行子类的实例创建。
相关代码:
- NewPizza
package factory.simplefatory.pizza;
public class NewPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备新的种类披萨");
}
}
- PizzaFactory
package factory.simplefatory.pizza;
public class PizzaFactory {
public static Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)) {
pizza = new CheesePizza();
} else if("greek".equals(orderType)) {
pizza = new GreekPizza();
} else if("new".equals(orderType)) {
pizza = new NewPizza();
}
return pizza;
}
}
- OrderPizza
package factory.simplefatory.order;
import factory.simplefatory.pizza.CheesePizza;
import factory.simplefatory.pizza.GreekPizza;
import factory.simplefatory.pizza.Pizza;
import factory.simplefatory.pizza.PizzaFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class OrderPizza {
OrderPizza() {
Pizza pizza = null;
while (true) {
String orderType = getOrderType();
pizza = PizzaFactory.createPizza(orderType);
if(pizza == null) {
System.out.println("没有你要的披萨");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
private String getOrderType() {
InputStream in = System.in;
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("请输入订购披萨类型");
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
类图如下:
可以看见OrderPizza直接依赖 PizzaFactory,由 PizzaFactory负责各类披萨实例的插件。这样就实现OrderPizza对披萨子类的解耦。
工厂方法模式
父类定义一个创建对象的抽象方法,由子类决定要实例化的类,对象的实例化推迟到子类。
新需求:披萨除了种类不同,也区分地区,比如:北京、上海
相关代码:
- 披萨类
package factory.fatorymethod.pizza;
public abstract class Pizza {
public abstract void prepare();
public void bake() {
System.out.println("烘烤披萨");
}
public void cut() {
System.out.println("切披萨");
}
public void box() {
System.out.println("打包披萨");
}
}
package factory.fatorymethod.pizza;
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京奶酪披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京希腊披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class SHCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备上海奶酪披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class SHGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备上海希腊披萨原材料");
}
}
- OrderPizza
package factory.fatorymethod.order;
import factory.fatorymethod.pizza.Pizza;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public abstract class OrderPizza {
public abstract Pizza createPizza(String orderType);
public String getOrderType() {
InputStream in = System.in;
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("请输入订购披萨类型");
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
- BJOrderPizza
package factory.fatorymethod.order;
import factory.fatorymethod.pizza.BJCheesePizza;
import factory.fatorymethod.pizza.BJGreekPizza;
import factory.fatorymethod.pizza.Pizza;
public class BJOrderPizza extends OrderPizza{
BJOrderPizza() {
while (true) {
String orderType = getOrderType();
Pizza pizza = this.createPizza(orderType);
if(pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("没有你要的披萨");
break;
}
}
}
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)) {
pizza = new BJCheesePizza();
} else if("greek".equals(orderType)) {
pizza = new BJGreekPizza();
}
return pizza;
}
}
- SHOrderPizza
package factory.fatorymethod.order;
import factory.fatorymethod.pizza.*;
public class SHOrderPizza extends OrderPizza{
SHOrderPizza() {
while (true) {
String orderType = getOrderType();
Pizza pizza = this.createPizza(orderType);
if(pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("没有你要的披萨");
break;
}
}
}
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)) {
pizza = new SHCheesePizza();
} else if("greek".equals(orderType)) {
pizza = new SHGreekPizza();
}
return pizza;
}
}
类图如下:
抽象工厂模式
定义一个父类工厂接口,定义创建实例的抽象方法,由子类工厂去实现。
代码如下:
- 披萨类
package factory.fatorymethod.pizza;
public abstract class Pizza {
public abstract void prepare();
public void bake() {
System.out.println("烘烤披萨");
}
public void cut() {
System.out.println("切披萨");
}
public void box() {
System.out.println("打包披萨");
}
}
package factory.fatorymethod.pizza;
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京奶酪披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京希腊披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class SHCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备上海奶酪披萨原材料");
}
}
package factory.fatorymethod.pizza;
public class SHGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备上海希腊披萨原材料");
}
}
- PizzaFactory
package factory.absfactory.factory;
import factory.absfactory.pizza.Pizza;
public interface PizzaFactory {
public abstract Pizza createPizza(String orderType);
}
- BJPizzaFactory
package factory.absfactory.factory;
import factory.absfactory.pizza.BJCheesePizza;
import factory.absfactory.pizza.BJGreekPizza;
import factory.absfactory.pizza.Pizza;
public class BJPizzaFactory implements PizzaFactory {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)) {
pizza = new BJCheesePizza();
} else if("greek".equals(orderType)) {
pizza = new BJGreekPizza();
}
return pizza;
}
}
- SHPizzaFactory
package factory.absfactory.factory;
import factory.absfactory.pizza.*;
public class SHPizzaFactory implements PizzaFactory {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if("cheese".equals(orderType)) {
pizza = new SHCheesePizza();
} else if("greek".equals(orderType)) {
pizza = new SHGreekPizza();
}
return pizza;
}
}
- OrderPizza
package factory.absfactory.order;
import factory.absfactory.factory.PizzaFactory;
import factory.absfactory.pizza.Pizza;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class OrderPizza {
private PizzaFactory pizzaFactory;
OrderPizza(PizzaFactory pizzaFactory) {
this.pizzaFactory = pizzaFactory;
while (true) {
String orderType = this.getOrderType();
Pizza pizza = pizzaFactory.createPizza(orderType);
if(pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("没有你要的披萨");
break;
}
}
}
public String getOrderType() {
InputStream in = System.in;
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("请输入订购披萨类型");
try {
return bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
- 测试类
public class Main {
public static void main(String[] args) {
// new OrderPizza(new BJPizzaFactory());
new OrderPizza(new SHPizzaFactory());
}
}
类图如下:
JDK使用的工厂模式
Calendar类使用简单工厂模式,Calendar的getInstance方法