设计模式
是对软件设计中普遍出现、反复出现的各种问题所提出的解决方案
学习设计模式的目的:
- 提高代码的重用性
- 提高代码的可读性
- 提高代码的可扩展性
- 提高代码的可靠性
七大设计原则
七大设计原则:
- 单一职责原则
- 接口隔离原则
- 依赖倒转(倒置)原则
- 里氏替换原则
- 开闭原则(
OCP
) - 迪米特法则
- 合成复用原则
一些性质的说明:
- 可重用性(相同的代码不需要多次编写)
- 可读性(编程的规范性,便于其他程序员阅读、理解)
- 可扩展性(增加新功能时能够很方便的进行)
- 可靠性(增加新功能时不能影响原有的功能)
- 尽量避免大量的
if-else if....else if ... else
的写法,这样写的耦合度太高了 - 总的目标是将变化的部分独立出来,针对接口进行编程,目标是解耦合
单一职责原则
一个类只负责一项职责,例如UserDao
只负责用户表的操作,不能负责其他表的操作
例子:
package com.xiaoxu.principle.singleresponsibility;
/**
* 单一职责
* @author xiaoxu
* @date 2020/9/1
*/
public class Main {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车");
vehicle.run("自行车");
vehicle.run("飞机");
}
}
/**
* 交通工具类
*/
class Vehicle {
public void run(String name) {
System.out.println(name + "在路上运行...");
}
}
以上程序的输出结果:
汽车在路上运行...
自行车在路上运行...
飞机在路上运行...
可以发现第三行不符合现实生活中所见到的情况,此时违反了单一职责的原则
解决方案2:
- 新建两个类,一个类为在路上跑的类,一个类为在天上飞的类
- 两个类同时提供
run(String)
方法 - 缺点:需要在Main类中实例化两个类、并且还要进行大量的修改
- 但此时符合了单一职责的原则
解决方案3:
- 直接在
Vehicle
类上修改,添加runWay(String)
(在路上运行)、runAir(String)
(在天上运行)方法 - 没有在类级别实现单一职责的原则,但在方法上实现了单一职责的原则
总结
- 能够降低变更引起的风险(例如解决方案2中,如果修改路上运行的就不会影响天空运行的)
- 通常情况下要遵守单一职责原则,只有逻辑比较简单时才违反单一职责原则,如果类中的方法比较少,可以在方法级别上遵守单一职责原则
接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
例子:

B、D需要实现这个接口,A依赖于1.2.3
方法,B依赖于1.4.5
方法
上图的实现代码:
package com.xiaoxu.principle.segregation;
/**
* 接口隔离原则
*
* @author xiaoxu
*/
public class Main {
public static void main(String[] args) {
}
}
class A {
public void depend1(Interface1 i) {
i.method1();
}
public void depend2(Interface1 i) {
i.method2();
}
public void depend3(Interface1 i) {
i.method3();
}
}
class C {
public void depend1(Interface1 i) {
i.method1();
}
public void depend4(Interface1 i) {
i.method4();
}
public void depend5(Interface1 i) {
i.method5();
}
}
class B implements Interface1 {
@Override
public void method1() {
System.out.println("method1");
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
class D implements Interface1 {
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
interface Interface1 {
void method1();
void method2();
void method3();
void method4();
void method5();
}
但上图不符合接口隔离原则,因为接口A、B仅依赖于接口中的部分方法,解决方案:
- 需要将接口1拆分开
- 拆分成三个接口,第一个接口仅包含
1
方法,第二个接口仅包含2.3
方法,第三个接口仅包含4.5
- 由于
A
与B
是通过1.2.3
进行使用的,所以B
只需要实现1.2
接口 - 同理,
D
只需要实现1.3
接口 - 尽量的减少不必要的实现
- 当一个类实现的多个接口中具有同名同参数的方法时,这个方法同时属于两个接口
拆分后的UML
类图:

代码:
package com.xiaoxu.principle.segregation;
/**
* 接口隔离原则
*
* @author xiaoxu
*/
public class Main {
public static void main(String[] args) {
}
}
class A {
public void depend1(Interface1 i) {
i.method1();
}
public void depend2(Interface2 i) {
i.method2();
}
public void depend3(Interface2 i) {
i.method3();
}
}
class C {
public void depend1(Interface1 i) {
i.method1();
}
public void depend4(Interface3 i) {
i.method4();
}
public void depend5(Interface3 i) {
i.method5();
}
}
class B implements Interface1, Interface2 {
@Override
public void method1() {
System.out.println("method1");
}
@Override
public void method2() {
}
@Override
public void method3() {
}
}
class D implements Interface1, Interface3 {
@Override
public void method1() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
interface Interface1 {
void method1();
}
interface Interface2 {
void method2();
void method3();
}
interface Interface3 {
void method4();
void method5();
}
依赖倒转原则
内容:
- 高层模块不应该依赖于底层模块,两者应该依赖于其抽象(也就是说依赖接口或者抽象类,而不是具体的实现类)
- 抽象不应该依赖于细节,细节应该依赖抽象
- 中心思想是面向接口编程
- 设计理念:相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的框架比以细节为基础搭建的框架稳定的多,在
java
中抽象是指接口或者抽象类,细节是指具体的实现类 - 使用接口或者抽象类的目的是制定好的规范,而不涉及任何具体的操作,把细节的展现交给他们的实现类完成
例子:
package com.xiaoxu.principle.inversion;
/**
* 依赖倒置原则
*/
public class Main {
public static void main(String[] args) {
Persion persion = new Persion();
persion.receive(new Email());
}
}
class Email {
public String getMessage() {
return "电子邮件内容";
}
}
class Persion {
public void receive(Email email) {
System.out.println(email.getMessage());
}
}
以上是一个电子邮件接收的例子,如果是接收短信、微信等消息还需要新建相应的类,并且还需要重载Persion
类中的receive
方法
解决思路:
-
引入一个接口,这个接口表示接收者,接口中带有一个
getMessage()
方法 -
package com.xiaoxu.principle.inversion; /** * 依赖倒置原则 */ public class Main { public static void main(String[] args) { Persion persion = new Persion(); persion.receive(new Email()); persion.receive(new Wechat()); } } class Email implements IReceiver { @Override public String getMessage() { return "电子邮件内容"; } } class Wechat implements IReceiver { @Override public String getMessage() { return "微信消息内容"; } } interface IReceiver { String getMessage(); } class Persion { public void receive(IReceiver receiver) { System.out.println(receiver.getMessage()); } }
-
也可以将
IReceiver
作为Persion
一个成员变量,通过Persion
类的构造器传入参数,再从这个类中的某个方法中调用接收消息的方法,比如Android
中的绑定服务采用此规则 -
也可以写一个接口,这个接口中写
setXXX
和相关的调用方法,调用时,先通过setXX
方法,再调用相关的方法就可以,比如Android
中的RecyclerView
的适配器的设置就是大致的遵循这个原则
里氏替换原则
是对如何规范继承所提出的规则
继承所包含的一层含义:凡是父类所实现好的方法实际上是在设定契约和规范,虽然不强制要求子类必须遵守这些规范,但子类如果把这些已经实现的方法任意修改,将会对继承的体系进行破坏。如果父类修改一些方法时,可能造成子类所依赖的这些方法的部分代码出错
遵循里氏替换原则就要求:子类尽量不要重写父类的方法或者尽量不用继承,采用依赖、聚合、聚合等关系进行替代
开闭原则
模块和函数应该对扩展开放,对修改关闭,也就是可以扩展一个类中的某些方法,但扩展后原有的代码不变,当软件需要变化时,尽量通过扩展进行实现变化,而不是通过修改代码实现变化
例子:
package com.xiaoxu.principle.ocp;
/**
* 开闭原则
*/
public class Main {
public static void main(String[] args) {
Sharp triangle = new Triangle();
Sharp rectangle = new Rectangle();
Main main = new Main();
main.draw(triangle);
main.draw(rectangle);
}
public void draw(Sharp sharp) {
if (sharp.type == 1) {
System.out.println("绘制三角形");
} else if (sharp.type == 2) {
System.out.println("绘制矩形");
}
}
public void drawTriangle() {
System.out.println("画三角形");
}
public void drawRectangle() {
System.out.println("画矩形");
}
}
class Sharp {
public int type;
}
// 三角形
class Triangle extends Sharp {
public Triangle() {
type = 1;
}
}
// 长方形
class Rectangle extends Sharp {
public Rectangle() {
type = 2;
}
}
以上代码违反了开闭原则,例如新增加一个椭圆形类所经过的步骤:
- 继承图形类
- 设置类型
- 在
Main
类中增加画椭圆的方法(违反开闭原则) - 修改
Main
类中的draw(Sharp sharp)
方法(违反开闭原则)
解决思路:
- 修改图形类为抽象类
- 抽象类中增加一个抽象方法为
draw()
方法 - 这时候,增加新的图形所需要的步骤:
- 继承图形类
- 设置类型
- 实现
draw()
方法
package com.xiaoxu.principle.ocp;
/**
* 开闭原则
*/
public class Main {
public static void main(String[] args) {
Sharp triangle = new Triangle();
Sharp rectangle = new Rectangle();
triangle.draw();
rectangle.draw();
}
}
abstract class Sharp {
public int type;
public abstract void draw();
}
// 三角形
class Triangle extends Sharp {
public Triangle() {
type = 1;
}
@Override
public void draw() {
System.out.println("画三角形");
}
}
// 长方形
class Rectangle extends Sharp {
public Rectangle() {
type = 2;
}
@Override
public void draw() {
System.out.println("画长方形");
}
}
迪米特法则
原则:
-
一个对象要对其他对象保持最少的了解
-
类与类关系越密切,耦合度越大
-
即类和类的调用通过
public
方法即可,不对外泄露其他信息- 自己的事情自己做
-
只与直接的朋友进行通信,直接的朋友是指:只要两个对象之间有耦合关系,那么这两个对象是朋友关系,耦合的方式有很多,例如依赖、关联、组合、聚合等,其中成员变量、方法的参数、方法的返回值为直接朋友,出现在局部变量中的类不是直接朋友
-
无论如何耦合关系都不能直接的避免,因此尽量的较少不与直接的朋友通信的代码
-
class T { // B类 为直接朋友 B a; // A类 为直接朋友 public void method(A a) { } // C类 为直接朋友 public C method() { } }
-
class T { public 返回值 method() { // E类 是局部变量,不是直接朋友 E e; } }
-
迪米特法则的例子:
class UserManager {
public List<User> getAllUser() {
具体的实现...
}
}
class Service {
public UserManager manager;
public void printAllUser() {
// 此时User 违反了迪米特法则
List<User> users = manager.getAllUser();
users.foreach(System.out :: println);
}
}
针对以上问题的解决方案:在UserManager
类中写printAllUser
class UserManager {
public List<User> getAllUser() {
具体的实现...
}
// 体现出来类和类的调用通过public方法,不对外泄露其他信息(List<User> 没有泄露)
public void printAllUser() {
List<User> users = getAllUser();
users.foreach(System.out :: println);
}
}
class Service {
public UserManager manager;
public void method() {
manager.printAllUser();
}
}
合成复用原则
尽量采用合成/聚合的方式,而不是使用继承
例如一个类中有多个方法,另外一个一个类只会用到其中的几个方法,这时候不要优先考虑继承的方式,解决方案如下图所示
分别是依赖、组合、聚合
UML
各种线的含义:
-
聚合:
A
类中有一个B
类的成员变量,并且是通过setter
进行赋值的 -
组合:
A
类中有一个B
类的成员变量,并且是实例化A
类时B
类成员变量一并创建-
例如
class A { public A a = new A(); }
-
画类图的时候,遵循属性: 类型
、方法: 返回值类型
依赖关系
只要在类中用到了对方,就代表产生了依赖关系
- 类中的成员变量、静态代码块
- 方法的参数、局部变量、返回值
public class Bean {
}
public class Dao {
public Bean save() {
return null;
}
}
public class Service {
public Bean bean;
public void method(Dao dao) {
}
}
Bean
和Service
、Dao
和Service
、Bean
和Dao
存在着依赖的关系,uml
可以画成:
泛化关系
是依赖关系的一个特殊的表现,如果有两个类,其中一个类是继承自另一个类,那么两者之间是泛化关系,抽象类的实现类也属于泛化关系
实现关系
接口被一个类实现
关联关系
依旧是依赖关系的一个特例,是类与类之间的关系,例如多对多,一对一
以下代码为单向一对一的关系
class A {
B b;
}
class B {
}
以下代码为双向一对一的关系
class A {
B b;
}
class B {
A a;
}
聚合关系
是关联关系的一个特例,表示表示整体和部分的关系,并且整体和部分是可以分开的(因为有setter
可以为某个成员变量进行赋值),具有导航性和多重性,A
类中有一个B
类的成员变量,并且是通过setter
进行赋值的
- 导航性是谁聚合谁,例如
A
类聚合到B
类中,
例如,人类有胳膊和脚,胳膊类和脚类聚合到人类中
/**
* 聚合关系
*/
public class Person {
private Arm arm;
private Foot foot;
public void setArm(Arm arm) {
this.arm = arm;
}
public void setFoot(Foot foot) {
this.foot = foot;
}
}
public class Foot {
}
public class Arm {
}
以上UML
类图为:
组合关系
当一个类销毁时,类中的某个类的成员变量也一并销毁,那么此时两个类为组合关系。A
类中有一个B
类的成员变量,并且是实例化A
类时B
类成员变量一并创建
下例中:Arm
和foot
不可分离并且生命周期和Person
类相同
public class Person {
private Arm arm = new Arm();
private Foot foot = new Foot();
}
public class Foot {
}
public class Arm {
}
uml
类图:
设计模式类型
创建型模式:
- 单例模式
- 抽象工厂模式
- 原型模式
- 建造者模式
- 工厂模式
结构型模式:
- 适配器模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
- 代理模式
行为模式:
- 模板方法模式
- 命令模式
- 访问者模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 职责链/责任链模式
单例模式
一个类中只能有一个实例,并且提供一个静态的方法获取这个对象的实例
单例模式总共8种写法:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
静态常量饿汉式
实现步骤:
- 构造器私有化
- 类的静态成员变量直接实例化
- 内部提供一个静态的
getInstance()
方法
代码示例如下:
package com.xiaoxu.principle.singleton;
/**
* 饿汉式静态常量
*/
public class Singleton01 {
// 实例化一个本类型的静态私有常量
private static final Singleton01 INSTANCE = new Singleton01();
// 私有化构造器
private Singleton01() {
}
/**
* 提供一个公共静态的方法返回实例
* @return Singleton01
*/
public static Singleton01 getInstance() {
return INSTANCE;
}
}
优点:写法比较简单,在类加载时完成了实例化,避免了线程同步的问题
缺点:没有达到懒加载的效果,如果自始至终没有使用,就相当于内存泄漏
静态代码块饿汉式
在上一个基础上,将实例化INSTANCE
放到静态代码块中
package com.xiaoxu.principle.singleton;
/**
* 饿汉式静态代码块
*/
public class Singleton02 {
// 定义一个本类型的静态私有常量
private static final Singleton02 INSTANCE;
// 使用静态代码块进行实例化
static {
INSTANCE = new Singleton02();
}
// 私有化构造器
private Singleton02() {
}
/**
* 提供一个公共静态的方法返回实例
*
* @return Singleton01
*/
public static Singleton02 getInstance() {
return INSTANCE;
}
}
优缺点和静态常量饿汉式一样
线程不安全懒汉式(不推荐)
步骤:
- 私有化构造器
- 创建私有静态本类的成员变量
- 提供静态公共的
getInstance
方法,如果静态变量为空就实例化,不为空直接返回,也就是使用到这个方法时采取创建实例
代码:
package com.xiaoxu.principle.singleton;
/**
* 懒汉式 线程不安全
*/
public class Singleton03 {
// 定义一个本类型的静态私有常量
private static Singleton03 INSTANCE;
// 私有化构造器
private Singleton03() {
}
/**
* 提供一个公共静态的方法返回实例
*
* @return Singleton01
*/
public static Singleton03 getInstance() {
// 如果是空的就进行实例化
if (INSTANCE == null) {
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}
有着非常严重的并发问题,以下为测试代码:
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
System.out.println("Singleton03.getInstance() = " + Singleton03.getInstance());
});
}
某次输出结果如下:
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@4b4b297b
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@2f75e5d2
Singleton03.getInstance() = com.xiaoxu.principle.singleton.Singleton03@7e5164c
可以发现,在多线程下每次获取到的实例并不一定是同一个
- 优点:实现了懒加载的效果,但只能在单线程下使用
- 缺点:多线程下可能会产生多个实例
- 在开发中不要使用这种方式
线程安全同步方法懒汉式(不推荐)
将getInstance
方法添加synchronized
改为同步的
package com.xiaoxu.principle.singleton;
/**
* 懒汉式 线程安全
*/
public class Singleton04 {
// 定义一个本类型的静态私有常量
private static Singleton04 INSTANCE;
// 私有化构造器
private Singleton04() {
}
/**
* 提供一个公共静态同步的方法返回实例
*
* @return Singleton01
*/
public static synchronized Singleton04 getInstance() {
// 如果是空的就进行实例化
if (INSTANCE == null) {
INSTANCE = new Singleton04();
}
return INSTANCE;
}
}
- 优点:解决了线程不安全问题
- 缺点:效率过低,每次都要加锁解锁
- 实际开发中不推荐使用这种方法
线程安全同步代码块懒汉式(不能使用,错误写法)
将getInstance()
方法中实例化instance
时的代码修改为同步代码块
package com.xiaoxu.principle.singleton;
/**
* 懒汉式 线程安全
*/
public class Singleton05 {
// 定义一个本类型的静态私有常量
private static Singleton05 INSTANCE;
// 私有化构造器
private Singleton05() {
}
/**
* 提供一个公共静态同步的方法返回实例
*
* @return Singleton01
*/
public static Singleton05 getInstance() {
// 如果是空的就进行实例化
if (INSTANCE == null) {
synchronized (Singleton05.class) {
INSTANCE = new Singleton05();
}
}
return INSTANCE;
}
}
如果多个线程进入if
语句并且当前的实例为空,多个线程将会依次的进行实例化,没有意义,以下为测试的输出结果:
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@55c8ecca
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@39e002e6
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
Singleton05.getInstance() = com.xiaoxu.principle.singleton.Singleton05@38f7ed39
- 这种方式不能起到解决线程安全的作用
双重检查(推荐)
解决了线程安全、懒加载、同步方法效率低的问题
volatile
中文易可挥发的,读音为ˈvɑːlətl
,当一个线程修改某个变量的值后,将会对其他线程是立即可见的,一些共享的变量会将暂时的缓存到其他的线程中,如果一个线程修改了值,对于其他线程可能不会立即更新,使用这个关键字后,一旦一个线程中改变了这个变量的值,其他线程中将会立即更新这个值
步骤:
-
将构造器私有化
-
将本类的静态常量添加
volatile
关键字 -
在
getInstance()
方法中按照:if(INSTANCE = null) { synchronized(锁) { if(instance == null) { 实例化Instance } } }
代码如下:
package com.xiaoxu.principle.singleton;
/**
* 懒汉式 双重检查
*/
public class Singleton06 {
// 定义一个本类型的静态私有常量
private static volatile Singleton06 INSTANCE;
// 私有化构造器
private Singleton06() {
}
/**
* 提供一个公共静态同步的方法返回实例
*
* @return Singleton01
*/
public static Singleton06 getInstance() {
// 如果是空的就进行实例化
if (INSTANCE == null) {
synchronized (Singleton06.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton06();
}
}
}
return INSTANCE;
}
}
静态内部类(推荐)
当外部类加载时,静态内部类不会加载
步骤:
- 私有化外部类的构造器
- 创建一个私有的内部静态类,添加一个外部类的静态常量并进行实例化
- 在外部类写一个公共静态的
getInstance()
方法,使其返回内部静态类中的常量
代码:
package com.xiaoxu.principle.singleton;
/**
* 饿汉式静态常量
*/
public class Singleton07 {
// 私有化构造器
private Singleton07() {
}
// 私有化静态内部类
private static class SingleInstance {
// 实例化一个外部类的静态私有常量
private static final Singleton07 INSTANCE = new Singleton07();
}
/**
* 提供一个公共静态的方法返回实例
*
* @return Singleton07
*/
public static Singleton07 getInstance() {
// 返回静态内部类的静态私有常量
return SingleInstance.INSTANCE;
}
}
- 优点:
- 采用类的加载机制保证了只有一个实例
- 只有调用
getInstance
才会实例化内部类 - 第一次加载时,由
JVM
保证线程安全 - 避免了线程不安全,实现了延迟加载
- 推荐使用
枚举(推荐)
也能实现单例模式,使用时直接使用类名.INSTANCE
package com.xiaoxu.principle.singleton;
public enum Singleton08 {
INSTANCE();
}
能够抗反射、解决线程同步等问题
工厂模式
简单工厂模式
又被称为静态工厂模式,可以把创建对象实例的方法改为静态的
由一个工厂对象决定创建出哪一个类型的产品实例,是工厂模式中最简单最实用的一个模式。定义一个创建对象的类,由这个类封装实例化对象的行为。应用场景:大量的创建某种、某类的对象时使用
一个需求:订购披萨
- 披萨有很多种类
- 披萨需要准备、烘烤、切割、打包
- 完成披萨订购
根据以上需求,写出对应的实体类,有一个抽象类披萨类,每个披萨的准备方法不一样,所以准备方法为抽象方法,有两个实现类,第一个为鸡肉披萨,第二个为牛奶披萨。还有一个订购披萨的类,根据传递的字符串完成某样披萨的订购,还有一个披萨商店类用来订购披萨

package com.xiaoxu.principle.factory.simplefactory;
public abstract class Pizza {
public String name;
public void setName(String name) {
this.name = name;
}
/**
* 准备原材料,不同的披萨原材料不同
*/
public abstract void prepare();
public void bake() {
System.out.println("烘烤披萨");
}
public void cut() {
System.out.println("切披萨");
}
public void box() {
System.out.println("打包披萨");
}
}
package com.xiaoxu.principle.factory.simplefactory;
public class MilkPizza extends Pizza{
@Override
public void prepare() {
System.out.println("牛奶披萨 正在准备");
}
}
package com.xiaoxu.principle.factory.simplefactory;
public class ChickenPizza extends Pizza{
@Override
public void prepare() {
System.out.println("鸡肉披萨 正在准备");
}
}
package com.xiaoxu.principle.factory.simplefactory;
public class OrderPizza {
public OrderPizza(String... pizzas) {
for (String s : pizzas) {
Pizza pizza = null;
if (s.equals("milk")) {
pizza = new MilkPizza();
pizza.setName("牛奶披萨");
} else if (s.equals("chicken")) {
pizza = new ChickenPizza();
pizza.setName("鸡肉披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
}
package com.xiaoxu.principle.factory.simplefactory;
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza("chicken", "milk", "chicken");
}
}
以上是相关的类:
- 优点:比较好理解、代码条理清晰
- 缺点:违反了开闭原则(扩展开放,对修改关闭)
- 如果再增加一个披萨的种类,则需要修改订购披萨类中的构造方法中的
if-else
条件- 如果披萨店有很多,每个店对披萨的制作方法不同,则还需要再每个店中的制作披萨的地方都要进行修改
- 如果再增加一个披萨的种类,则需要修改订购披萨类中的构造方法中的
- 修改思路:把创建披萨的对象封装到一个类中,如果新增加一个披萨种类,只需要修改这个类即可,其他类不用修改了
UML类图

- 新建一个简单工厂类,提供创建披萨的方法
- 在这个方法中根据披萨名称返回
Pizza
实例
- 在这个方法中根据披萨名称返回
- 订购披萨类中聚合简单工厂
之后再新增一个披萨时,只需要修改简单工厂中的创建披萨方法就可以了
代码为:
package com.xiaoxu.principle.factory.simplefactory;
public class OrderPizza {
private SimplePizzaFactory simplePizzaFactory;
public void setSimplePizzaFactory(SimplePizzaFactory simplePizzaFactory) {
this.simplePizzaFactory = simplePizzaFactory;
}
public OrderPizza(SimplePizzaFactory simplePizzaFactory) {
this.simplePizzaFactory = simplePizzaFactory;
}
public void orderPizza(String... pizzas) {
for (String s : pizzas) {
Pizza pizza = simplePizzaFactory.createPizza(s);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
}
}
package com.xiaoxu.principle.factory.simplefactory;
public class SimplePizzaFactory {
public Pizza createPizza(String name) {
Pizza pizza = null;
if ("milk".equals(name)) {
pizza = new MilkPizza();
pizza.setName("牛奶披萨");
} else if ("chicken".equals(name)) {
pizza = new ChickenPizza();
pizza.setName("鸡肉披萨");
}
return pizza;
}
}
- 也可以把简单工厂的
createPizza(String)
方法改为静态的方法,不过可能一个商店想要换其他的工厂生产披萨,在更换工厂时可以使用实例与之对应
工厂方法模式
工厂方法模式:
- 创建一个基类,将这个类设置为抽象的,提供一个返回实例的抽象方法
- 子类重谢这个抽象方法
限制有了新的需求,如果点披萨时,可以去不同地区的披萨店点,解决思路:
- 创建多个工厂类,每个工厂类对应着一个不同的地区
- 这个思路是可行的,但如果地区一旦变多,项目一旦变大,这个思路的可维护性和可扩展性并不好
- 采用工厂方法模式
- 创建一个抽象的工厂类,将返回
pizza
实例的方法设为抽象的 - 新建多个披萨类,例如北京牛奶披萨、北京鸡肉披萨、天津牛奶披萨、天津鸡肉披萨
- 新建其他地区的工厂,并实现抽象工厂类
- 重写返回
pizza
实例的方法,将其返回各自地区的相关类型pizza
OrderPizza
实例代码不变,将工厂的类型改为抽象工厂类
- 创建一个抽象的工厂类,将返回
总体上相当于每个地区使用工厂方法,每个地区的Pizza
使用简单工厂
抽象工厂模式
相当于简单工厂模式和工厂方法模式的一个整合,定义了一个接口,接口中抽象了一个返回实例的方法,可以看成对简单工厂的进一步抽象。分为两层:抽象层和实现层
如果使用这种方式实现订购不同地区的披萨,UML
类图为:

Calendar.getInstance();
采用的是简单工厂的方式获取的实例
原型模式
拷贝对象
Java clone
对象:
-
实现
Cloneable
接口,这个接口属于标记接口 -
重写
clone
方法,因为这个方法是protected
的权限,需要覆盖一下才能够被本包中的其他类使用-
@Override public Object clone() throws CloneNotSupportedException { return super.clone(); }
-
-
这种方式克隆的对象是浅拷贝,整个实例的引用是一个新的,但实例内部的成员变量如果是引用类型时依然是旧的,也就是如果是基本类型,直接把值传递过去,如果是引用类型,则将会把引用值传递过去
实现深拷贝
方式1:实现Cloneable
接口的基础上针对引用类型、引用类型的引用类型…进行单独处理,也就是有多少个就处理多少个,比较麻烦
对象的序列化实现深拷贝
也就是使用对象流,写入时放到内存中,读取时在内存中读取
package com.xiaoxu.principle.prototype;
import java.io.*;
public class User implements Cloneable, Serializable {
String s;
StringBuilder sss = new StringBuilder();
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public User deepCopy() {
User user = null;
try {
// 字节数组输出流暂时的存储当前对象的二进制内容
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 对象构建输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
// 将当前对象输出到字节数组输出流中
objectOutputStream.writeObject(this);
// 字节数组输入流,将字节数组输出流中的内容读入
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
// 对象输入流,用来序列化一个对象
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// 序列化一个对象
user = (User) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return user;
}
}
建造者模式
又被称为生成器模式,将复杂对象的构建过程抽象出来,一步一步的构建一个对象,对于必须的属性可以放到一个构造器中
核心角色:
product
产品角色,一个具体的产品对象builder
抽象建造者,创建一个product
的各个部件的接口,组合了product
ConcreteBuilder
具体建造者,实现接口、构建、装配各个配件,也就是建造的具体细节,继承了builder
director
指挥者,构建一个builder
接口的对象,用来创建一个复杂的对象,主要隔离客户与对象的生产过程(底层实现细节)、负责控制产品对象的生产过程,聚合了builder

例子:盖房子
房子有普通房子、高的房子,每个房子都可以打地基、砌墙、封顶
abstract class AbsHouse {
打地基抽象方法();
砌墙抽象方法();
封顶抽象方法();
build抽象方法() {
打地基抽象方法();
砌墙抽象方法();
封顶抽象方法();
}
}
class 普通房子 继承 AbsHouse {
打地基方法() {
}
砌墙抽象方法() {
}
封顶抽象方法() {
}
}
class 高的房子 继承 AbsHouse {
打地基方法() {
}
砌墙抽象方法() {
}
封顶抽象方法() {
}
}
以上关于房屋的一个建造的代码,但这个代码的构建过程是固定的,只能从打地基->砌墙->封顶的过程进行执行
使用建造者模式进行修改:
- 定义一个房屋实体类
- 抽象一个房屋建造者类,组合房屋实体类,提供一个
build()
方法用来返回房屋实体类 - 对于不同种类的房屋,继承房屋建造者类,实现抽象方法
- 新建一个指挥者类,聚合房屋建造者
- 在指挥者类中的相关方法自行建造
UML
图

package com.xiaoxu.principle.builder;
// 产品
public class House {
// 这个属性是必须的
private String name;
// 地基
private String foundation, wall, top;
public String getName() {
return name;
}
public House(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getFoundation() {
return foundation;
}
public void setFoundation(String foundation) {
this.foundation = foundation;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getTop() {
return top;
}
public void setTop(String top) {
this.top = top;
}
@Override
public String toString() {
return "House{" +
"name='" + name + '\'' +
", foundation='" + foundation + '\'' +
", wall='" + wall + '\'' +
", top='" + top + '\'' +
'}';
}
}
package com.xiaoxu.principle.builder;
public abstract class HouseBuilder {
// 聚合House
protected House house;
public HouseBuilder(String name) {
this.house = new House(name);
}
public abstract void foundationBuild();
public abstract void topBuild();
public abstract void wallBuild();
public House build() {
return house;
}
}
package com.xiaoxu.principle.builder;
// 高房子
public class HighHouseBuilder extends HouseBuilder {
public HighHouseBuilder(String name) {
super(name);
}
@Override
public void foundationBuild() {
System.out.println("高房子盖地基");
house.setFoundation("高房子的地基");
}
@Override
public void topBuild() {
System.out.println("高房子封顶");
house.setFoundation("高房子的封顶");
}
@Override
public void wallBuild() {
System.out.println("高房子盖墙");
house.setFoundation("高房子的墙");
}
}
package com.xiaoxu.principle.builder;
// 普通房子
public class OrdinaryHouseBuilder extends HouseBuilder {
public OrdinaryHouseBuilder(String name) {
super(name);
}
@Override
public void foundationBuild() {
System.out.println("普通房子盖地基");
house.setFoundation("普通房子的地基");
}
@Override
public void topBuild() {
System.out.println("普通房子封顶");
house.setFoundation("普通房子的封顶");
}
@Override
public void wallBuild() {
System.out.println("普通房子盖墙");
house.setFoundation("普通房子的墙");
}
}
package com.xiaoxu.principle.builder;
// 指挥者
public class HouseDirector {
HouseBuilder houseBuilder;
// 聚合建造者
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
// 建造房子,里边的具体步骤可以自定义
public void makeHouse() {
houseBuilder.foundationBuild();
houseBuilder.wallBuild();
houseBuilder.topBuild();
House house = houseBuilder.build();
System.out.println(house);
}
}
package com.xiaoxu.principle.builder;
public class Main {
public static void main(String[] args) {
HouseDirector houseDirector = new HouseDirector(new HighHouseBuilder("高高的房子"));
houseDirector.makeHouse();
}
}
建造者模式(链式)
链式建造者模式的自由度更高,可以由用户一步步的构建一个对象
- 私有化实体类的构造方法
- 成员变量根据情况进行私有化,且根据情况不提供
setter
- 新建一个公共静态的内部类,如果实体类中有必须的参数,那么在公共静态内部类的构造器中需要提供这个参数
- 将实体类中的成员变量复制到内部类中
- 将提实体类的构造方法的参数修改为内部类,并将内部类中的值在构造器中逐一赋值
- 在内部类中提供对各个参数进行赋值的方法,并使其返回值类型为内部类
- 内部类中提供
build()
方法返回实体类的实例
package com.xiaoxu.principle.builder;
public class House2 {
// 这个属性是必须的
private String name;
// 地基
private String foundation, wall, top;
@Override
public String toString() {
return "House2{" +
"name='" + name + '\'' +
", foundation='" + foundation + '\'' +
", wall='" + wall + '\'' +
", top='" + top + '\'' +
'}';
}
private House2(Builder builder) {
this.name = builder.name;
this.foundation = builder.foundation;
this.wall = builder.wall;
this.top = builder.top;
}
public static class Builder {
// 这个属性是必须的
private String name;
// 地基
private String foundation, wall, top;
// 为必须的参数赋值
public Builder(String name) {
this.name = name;
}
public House2.Builder setFoundation(String foundation) {
this.foundation = foundation;
return this;
}
public House2.Builder setWall(String wall) {
this.wall = wall;
return this;
}
public House2.Builder setTop(String top) {
this.top = top;
return this;
}
public House2 build() {
return new House2(this);
}
}
}
package com.xiaoxu.principle.builder;
public class Main2 {
public static void main(String[] args) {
House2 house2 = new House2.Builder("大大的房子")
.setFoundation("大大的地基")
.setTop("大大的顶部")
.setWall("大大的墙")
.build();
System.out.println("house2 = " + house2);
}
}
也可以按照需求,将内部类中的方法放到接口中
适配器模式
将某个类的接口转换成另一个类的所期望的一个格式,目的是为了兼容,让原本不匹配的接口、类可以一起工作,属于结构模型模式,分为类适配器模式、接口适配器模式、对象适配器模式
工作原理:
- 将一个类的接口转换为另一种接口,让原本不兼容的类进行兼容
- 用户层面看不到适配器,是解耦的
- 用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口方法
类适配器
适配器类通过继承源类,实现目标类的接口,就可以完成源类适配目标类,目标类可以聚合接口或者适配器完成使用
uml
类似于
对象适配器
符合合成复用原则,是一种常用的适配器模式
适配器类不再继承源类,而是直接聚合源类,并且实现接口

接口适配器模式
有时被称为适配器模式或者接口适配器模式
当不需要全部实现接口所提供的所有方法时,可以先设计一个抽象类实现接口,并且为这个接口中的方法做一个默认实现(空的方法),抽象类的子类可以针对实际情况,自行的选择实现的方法
应用场景:Android中EditText
的内容发生改变的是按(可以仅用来覆盖某几个方法),这个接口中有许多的方法,通常只用其中的几个,用不到的留空(空实现)
这种设计模式也是比较常用的
package com.xiaoxu.principle.adapter.interfaceadapter;
public interface Interface {
void method1();
void method2();
void method3();
void method4();
void method5();
void method6();
void method7();
}
package com.xiaoxu.principle.adapter.interfaceadapter;
public class AbsClass implements Interface{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
@Override
public void method6() {
}
@Override
public void method7() {
}
}
package com.xiaoxu.principle.adapter.interfaceadapter;
// 子类自行的选择实现的方法
public class SubClass extends AbsClass {
@Override
public void method2() {
super.method2();
}
@Override
public void method5() {
super.method5();
}
}
桥接模式
桥接模式bridge,将实现和抽象放到两个不同的层次中,两个层次可以独立的改变,是一种结构型号的设计模式,是基于类最小的原则(扩展时尽量少增加类),通过封装、聚合、继承等行为让不同的类实现不同的职责,特点是把抽象和行为实现分离开,保持各部分的独立性和功能的扩展
UML类图如下:
抽象类中聚合接口,而这个接口的实例就是充当的桥的作用
例子:
- 手机种类有很多,有折叠式、直立式、翻转式
- 不同种类的手机都有打电话、发短信等功能
- 不同的手机品牌都有折叠式、直立式、翻转式的手机
最直接了当的解决方案的uml
类如下图所示:
出现了类爆炸的问题,每增加一个种类,就要增加相应的品牌,每增加一个品牌,就要在每个种类下增加一个品牌
这个例子如果使用桥接模式,那么功能可以作为一个接口,手机品牌作为接口的实现类。手机作为一个抽象类,不同种类的手机继承这个抽象类
因此,uml
类图为:
代码实现:
package com.xiaoxu.principle.bridge;
// 功能
public interface Function {
void turnOn();
void turnOff();
void call();
}
package com.xiaoxu.principle.bridge;
public class Xiaomi implements Function{
@Override
public void turnOn() {
System.out.println("小米手机开机");
}
@Override
public void turnOff() {
System.out.println("小米手机关机");
}
@Override
public void call() {
System.out.println("小米手机打电话");
}
}
package com.xiaoxu.principle.bridge;
public class Vivo implements Function{
@Override
public void turnOn() {
System.out.println("Vivo手机开机");
}
@Override
public void turnOff() {
System.out.println("Vivo手机关机");
}
@Override
public void call() {
System.out.println("Vivo手机打电话");
}
}
package com.xiaoxu.principle.bridge;
public abstract class Phone {
public Phone(Function function) {
this.function = function;
}
private final Function function;
public void turnOn() {
function.turnOn();
}
public void turnOff() {
function.turnOn();
}
public void call() {
function.call();
}
}
package com.xiaoxu.principle.bridge;
// 折叠
public class FoldablePhone extends Phone{
public FoldablePhone(Function function) {
super(function);
}
@Override
public void call() {
System.out.print("折叠式");
super.call();
}
@Override
public void turnOff() {
System.out.print("折叠式");
super.turnOff();
}
@Override
public void turnOn() {
System.out.print("折叠式");
super.turnOn();
}
}
package com.xiaoxu.principle.bridge;
// 直立式
public class ErectPositionPhone extends Phone{
public ErectPositionPhone(Function function) {
super(function);
}
@Override
public void call() {
System.out.print("直立式");
super.call();
}
@Override
public void turnOff() {
System.out.print("直立式");
super.turnOff();
}
@Override
public void turnOn() {
System.out.print("直立式");
super.turnOn();
}
}
在以上代码结构中,如果增加一个种类,只需要一个新的类继承Phone
即可,无需再添加其他的手机。如果增加一个手机只需要一个新的类实现功能(Function
)接口,无需在进行其他的操作
应用场景
JDBC
驱动- 银行转账系统
- 转账的分类:ATM、柜台、网上转账
- 转账的用户类型:金卡、银卡、普通卡、钻石卡
- 消息的管理
装饰者模式
动态的将新功能附加到对象上,在对象扩展方面要比继承更有弹性,符合OCP原则
- 主体是被装饰者
- 包装是装饰者
多个装饰者之上还可以设置一个缓冲层(一个类),使其后的类都继承自这个类
如下图,装饰者和被装饰者是继承+组合的关系,右半部分的装饰者又是一个被装饰者,而被装饰者又是一个装饰者,左半部分仅仅是一个被装饰者,因为装饰者中带有被装饰者的实例
uml
类图:
装饰者
现在有这么个需求:
- 咖啡店里有许多种类咖啡
- 每个种类的咖啡价格不同
- 每个咖啡里边可以添加配料
- 每种配料可以和任意的咖啡进行组合,并且价格不同
- 要求计算总的价格
解决方案1:
- 抽象一个咖啡类,提供一个计算价格的方法,让子类实现价格计算的方法
- 同时新建多个配料类
- 通过排列组合枚举所有情况,进行计算价格
- 使用这种方式效率比较低,扩展不方便
解决方案2:
- 抽象一个咖啡类,在咖啡类中将所有配料写进去,配料设置为整型,为0代表没有点,为其他数字代表点了相应的数量
- 当需要增加或者删除种类时,代码改动较大
- 违反了开闭原则
解决方案3:
- 抽象一个饮料类,提供抽象的返回价格的方法
- 抽象一个咖啡类并且继承自饮料类
- 抽象一个装饰者类(配料类),继承自饮料类,同时提供一个饮料类型的成员变量
- 配料继承装饰者类(配料类)并实现计算价格的方法,此时需要递归的调用饮料类型的成员变量的计算价格的方法
- 咖啡继承咖啡类实现相应的方法
代码:
package com.xiaoxu.principle.decorator;
public abstract class Drink {
protected String introduce;
protected int count = 1;
public abstract int cost();
public String getIntroduce() {
return this.introduce;
}
public Drink() {
}
public Drink(int count) {
this.count = count;
}
}
package com.xiaoxu.principle.decorator;
public abstract class Coffee extends Drink {
public Coffee(int count) {
introduce = "咖啡";
this.count = count;
}
}
package com.xiaoxu.principle.decorator;
// 装饰者
public abstract class Decorator extends Drink{
protected Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
public Decorator(Drink drink, int count) {
super(count);
this.drink = drink;
}
}
package com.xiaoxu.principle.decorator;
// 装饰者
public class AppleDecorator extends Decorator{
public AppleDecorator(Drink drink) {
super(drink);
introduce = "苹果配料(1元)";
}
public AppleDecorator(Drink drink, int count) {
super(drink, count);
introduce = "苹果配料(1元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " + ");
return count * 1 + drink.cost();
}
}
package com.xiaoxu.principle.decorator;
// 装饰者
public class BananaDecorator extends Decorator{
public BananaDecorator(Drink drink) {
super(drink);
introduce = "香蕉配料(3元)";
}
public BananaDecorator(Drink drink, int count) {
super(drink, count);
introduce = "香蕉配料(3元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " + ");
return count * 3 + drink.cost();
}
}
package com.xiaoxu.principle.decorator;
// 装饰者
public class MilkDecorator extends Decorator{
public MilkDecorator(Drink drink) {
super(drink);
introduce = "牛奶配料(2元)";
}
public MilkDecorator(Drink drink, int count) {
super(drink, count);
introduce = "牛奶配料(2元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " + ");
return count * 2 + drink.cost();
}
}
package com.xiaoxu.principle.decorator;
public class BlackCoffee extends Coffee{
public BlackCoffee(int count) {
super(count);
introduce = "黑" + introduce + "(10元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " = ");
return 10 * count;
}
}
package com.xiaoxu.principle.decorator;
public class SweetCoffee extends Coffee {
public SweetCoffee(int count) {
super(count);
introduce = "甜" + introduce + "(15元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " = ");
return 15 * count;
}
}
package com.xiaoxu.principle.decorator;
public class WhiteCoffee extends Coffee {
public WhiteCoffee(int count) {
super(count);
introduce = "白" + introduce + "(20元)";
}
@Override
public int cost() {
System.out.print(introduce + " * " + count + " = ");
return 20 * count;
}
}
package com.xiaoxu.principle.decorator;
public class Main {
public static void main(String[] args) {
System.out.println(new AppleDecorator(new BananaDecorator(new BlackCoffee(1))).cost());
System.out.println(new BananaDecorator(new AppleDecorator(new MilkDecorator(new WhiteCoffee(2)), 3)).cost());
}
}
输出结果:
苹果配料(1元) * 1 + 香蕉配料(3元) * 1 + 黑咖啡(10元) * 1 = 14
香蕉配料(3元) * 1 + 苹果配料(1元) * 3 + 牛奶配料(2元) * 1 + 白咖啡(20元) * 2 = 48
例如第一个结果的装饰模式是按照下图进行装饰的:
此后进行扩展(加入新的咖啡或者配料)只需要扩展相应的父类即可,无需修改其他地方的代码
组合模式
又称为部分整体模式,描述的是部分和整体的关系,创建了对象组的树形结构,将对象以树形结构表示,仍然属于结构性模式,可以使得用户以一致性的方式处理对象
解决的是当我们的对象可以生成树形结构、要对树上的节点和叶子进行操作时能提供一致的方式
-
组件 (Component) 接口描述了树中简单项目和复杂项目所共有的操作。组件可以是抽象类或者接口
-
叶节点 (Leaf) 是树的基本结构, 它不包含子项目。
一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。
-
容器 (Container)——又名 “组合 (Composite)”——是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。
容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。
Collection
整个集合就是采用组合模式的,Collection
->List
->ArrayList
例子:大学由一个组织进行管理,大学中包括学院,学院中包括专业,三个类都是组织的子类或者实现类,大学中包括学院 通过聚合组织的方式进行管理,学院中包括专业 同样也通过聚合组织的方式进行管理
大致的代码:
package com.xiaoxu.principle.combination;
public abstract class Component {
public abstract void add(Component component);
public abstract void remove();
public abstract void print();
}
package com.xiaoxu.principle.combination;
import java.util.List;
public class University extends Component{
List<Component> list;
@Override
public void add(Component component) {
}
@Override
public void remove() {
}
@Override
public void print() {
}
}
package com.xiaoxu.principle.combination;
import java.util.List;
public class College extends Component {
List<Component> list;
@Override
public void add(Component component) {
}
@Override
public void remove() {
}
@Override
public void print() {
}
}
package com.xiaoxu.principle.combination;
import java.util.List;
public class Department extends Component{
@Override
public void add(Component component) {
}
@Override
public void remove() {
}
@Override
public void print() {
}
}
之后添加数据时,只要是Component
的子类,就可以直接添加进去
外观模式
外观模式(facade
)也被称为过程模式,为子系统的一组接口提供一致的界面,可以隐藏底层实现细节,使得调用端只和接口进行通信,解决多个复杂接口带来的使用困难,简化用户操作
也就是把一组同时执行的方法封装到一个类中的某个方法中,和这个方法同时执行
由一个控制类完成对其他几个类的调用
例子:回家之后会完成打开灯、打开窗户、打开电视、打开风扇,睡觉时会关灯、关电视,出门时会关窗户、关灯、关风扇、电视断电
创建4个类,分别对应:灯、窗户、电视、风扇。创建一个控制类,提供3个方法:回家、睡觉、出门
代码:
package com.xiaoxu.principle.facade;
public class Fan {
public void open() {
System.out.println("打开风扇");
}
public void close() {
System.out.println("风扇断电");
}
}
package com.xiaoxu.principle.facade;
public class Light {
public void open() {
System.out.println("打开灯");
}
public void close() {
System.out.println("关闭灯");
}
}
package com.xiaoxu.principle.facade;
public class Tv {
public void open() {
System.out.println("打开电视");
}
public void close() {
System.out.println("电视断电");
}
public void pause() {
System.out.println("关闭电视");
}
}
package com.xiaoxu.principle.facade;
public class Window {
public void open() {
System.out.println("打开窗户");
}
public void close() {
System.out.println("关闭窗户");
}
}
package com.xiaoxu.principle.facade;
public class Control {
public Fan fan;
public Light light;
public Window window;
public Tv tv;
public Control(Fan fan, Light light, Window window, Tv tv) {
this.fan = fan;
this.light = light;
this.window = window;
this.tv = tv;
}
public void goHome() {
System.out.println("--------回家--------");
fan.open();
light.open();
window.open();
tv.open();
}
public void goSleep() {
System.out.println("--------睡觉--------");
light.close();
tv.pause();
}
public void outHome() {
System.out.println("--------出门--------");
fan.close();
light.close();
window.close();
tv.close();
}
}
package com.xiaoxu.principle.facade;
// 外观模式
public class Main {
public static void main(String[] args) {
Control control = new Control(new Fan(), new Light(), new Window(), new Tv());
control.goHome();
control.goSleep();
control.outHome();
}
}
享元模式
享元模式(flyweight
)也被称为蝇量模式,运用共享的方式有效的支持大量的细粒度的对象,能够解决重复对象浪费内存的问题,当系统中有大量的相似对象,需要缓冲池时,可以在缓冲池中取出相应的对象,从而降低内存、提高效率。应用场景:各种池,例如String
常量池、数据连接池、缓冲池等,享元模式是池技术的重要实现方式
分为
- 享元工厂
- 用来返回享元角色的实例
- 通常有一个集合存储这些实例
- 用来当作池
- 抽象享元角色
- 抽象类
- 具体的享元角色
- 抽象类的子类,可以由享元工厂返回
- 不可共享的享元角色
- 一般不会放到享元工厂中
例如五子棋有黑棋、白棋,每个棋子的实例是可以复用的
- 抽象一个棋子类
- 实现棋子类
- 编写一个工厂类,使用
Map
以键的形式存储实例 - 工厂类中提供通过名称获取实例的方法,如果键不存在时,将会创建一个新实例并放到
Map
中
package com.xiaoxu.principle.flyweight;
public abstract class Chess {
public abstract void use();
}
package com.xiaoxu.principle.flyweight;
public class WhiteAndBlackChess extends Chess{
@Override
public void use() {
System.out.println("使用了棋子");
}
}
package com.xiaoxu.principle.flyweight;
import java.util.HashMap;
public class Factory {
private HashMap<String, WhiteAndBlackChess> map = new HashMap<>();
public Chess getChess(String name) {
if (map.containsKey(name)) {
return map.get(name);
}
WhiteAndBlackChess value = new WhiteAndBlackChess();
map.put(name, value);
System.out.println("创建" + name + "实例");
return value;
}
}
package com.xiaoxu.principle.flyweight;
public class Main {
public static void main(String[] args) {
Factory factory = new Factory();
Chess blackChess = factory.getChess("黑棋");
blackChess.use();
Chess blackChess2 = factory.getChess("黑棋");
blackChess2.use();
Chess whiteChess = factory.getChess("白棋");
whiteChess.use();
}
}
代理模式
为一个对象提供一个替身以控制这个对象的访问,可以在目标对象的实现基础上增加和扩展额外的功能,分为:静态代理、动态代理(JDK
代理、接口代理)、Cglib
代理(在内存中动态的创建对象,而不需要实现接口,属于动态代理的范畴)
静态代理
使用时,需要定义接口或者父类,被代理的对象以及代理的对象需要共同实现接口或者继承父类,再通过相同的方法调用目标对象的方法
代理类通过接口进行聚合被代理的类
package com.xiaoxu.principle.proxy.staticproxy;
public interface Interface {
void method();
}
package com.xiaoxu.principle.proxy.staticproxy;
public class Target implements Interface{
@Override
public void method() {
System.out.println("这是一个方法");
}
}
package com.xiaoxu.principle.proxy.staticproxy;
public class Proxy implements Interface {
public Proxy(Interface in) {
this.in = in;
}
private final Interface in;
@Override
public void method() {
System.out.println("代理之前");
in.method();
System.out.println("代理之后");
}
}
package com.xiaoxu.principle.proxy.staticproxy;
public class Main {
public static void main(String[] args) {
Proxy proxy = new Proxy(new Target());
proxy.method();
}
}
- 优点:不修改目标对象的前提下通过代理对象对目标功能进行扩展
- 缺点:代理对象需要和被代理对象实现相同的接口,因此可能存在许多类,并且每个方法都需要实现一遍,一旦接口进行修改,代理对象和被代理对象都要修改
动态代理
代理对象不需要实现接口,被代理的对象需要实现接口,否则不能使用动态代理,代理对象的生成是使用的jdk
提供的api
进行的,在内存中构建对象,所以动态代理也被称为jdk
代理或者接口代理
根据传入的对象利用反射机制返回一个代理对象,通过代理对象调用被代理的方法
被代理的类需要实现一个接口,代理工厂类通过接口聚合被代理的类,提供一个getProxyInstance()
方法,即获取一个代理对象的方法,返回值可以直接为Object
可以在客户端进行强转,内部使用JDK
的java.lang.reflect.Proxy
类所提供的静态方法:
-
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
进行返回,第一个参数为类加载器,可以直接使用ClassLoader.ClassLoader.getSystemClassLoader()
或者聚合的接口成员变量.getClass().getClassLoader()
,第二个参数需要填入一个接口的Class
数组,可以直接使用聚合的接口成员变量.getClass().getInterfaces()
或者手动指定接口.class
,第三个参数为InvocationHandler
(调用处理程序)的一个接口,可以直接使用lambda
表达式,需要实现:
-
Object invoke(Object proxy, Method method, Object[] args)
参数1为生成的代理对象;参数2为Method
实例,代表当前所执行的方法;参数3代表调用当前方法所传入的参数
package com.xiaoxu.principle.proxy.dynamic;
public interface Interface {
void method();
void method2();
}
package com.xiaoxu.principle.proxy.dynamic;
public class Target implements Interface {
@Override
public void method() {
System.out.println("这是一个方法");
}
@Override
public void method2() {
System.out.println("这是method2");
}
}
package com.xiaoxu.principle.proxy.dynamic;
public class Target implements Interface {
@Override
public void method() {
System.out.println("这是一个方法");
}
@Override
public void method2() {
System.out.println("这是method2");
}
}
package com.xiaoxu.principle.proxy.dynamic;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Interface in;
public ProxyFactory(Interface in) {
this.in = in;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
in.getClass().getInterfaces(),
(proxy, method, args) -> {
if ("method2".equals(method.getName())) {
System.out.println("即将代理第二个方法");
} else {
System.out.println("代理其他方法");
}
System.out.println("动态代理开始");
Object invoke = method.invoke(in, args);
System.out.println("动态代理结束");
return invoke;
});
}
}
package com.xiaoxu.principle.proxy.dynamic;
// 动态代理
public class Main {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new Target());
Interface proxyInstance = (Interface) proxyFactory.getProxyInstance();
proxyInstance.method2();
}
}
Cglib代理
也称为子类代理,生成的代理对象相当于被代理类的子类,所以这个类不能被final
所标注,如果是final/static
的方法也无法被代理,用于当需要代理没有实现任何接口的对象
- 被代理的对象需要实现接口使用动态代理(
JDK
代理) - 被代理的对象不需要实现接口使用
Cglib
代理
引入依赖:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.ant/ant-launcher -->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.10.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.ant/ant -->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.12</version>
</dependency>
底层原是使用字节码处理框架ASM
来转换字节码并实现新的类,所以如果执行代码是抛出以下类似的异常,则需要添加一些启动参数
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.xiaoxu.principle.proxy.cglib.ProxyFactory.getProxyInstance(ProxyFactory.java:18)
at com.xiaoxu.principle.proxy.cglib.Main.main(Main.java:6)
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @593634ad
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:174)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:153)
at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73)
... 2 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @593634ad
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at net.sf.cglib.core.ReflectUtils$1.run(ReflectUtils.java:61)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
at net.sf.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:52)
at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
... 14 more

启动参数:
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
代码:
package com.xiaoxu.principle.proxy.cglib;
public class MyClass {
public void method() {
System.out.println("这是方法");
}
public void method2() {
System.out.println("这是方法2");
}
}
package com.xiaoxu.principle.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyFactory implements MethodInterceptor {
private Object myObject;
public ProxyFactory(Object myObject) {
this.myObject = myObject;
}
public Object getProxyInstance() {
// 创建工具类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(myObject.getClass());
// 设置回调,MethodInterceptor实现类回调接口
enhancer.setCallback(this);
// 返回代理对象
return enhancer.create();
}
// 调用被代理对象的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("开始代理");
Object invoke = method.invoke(myObject, objects);
System.out.println("结束代理");
return invoke;
}
}
package com.xiaoxu.principle.proxy.cglib;
public class Main {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new MyClass());
MyClass proxy = (MyClass) proxyFactory.getProxyInstance();
proxy.method();
}
}
应用场景
- 防火墙代理
- 缓存代理
- 远程代理
- 同步代理
模板方法模式
模板方法又称为模板模式,在一个抽象类中公开的定义了方法模板,子类可以根据需要重写相关的方法,属于行为型模式
- 抽象类中实现了模板方法,定义了抽象方法,可以定义为
final
template()
方法是模板方法,这个方法中将会按自定义的顺序调用抽象的方法- 子类继承并实现抽象方法
例子:洗衣服需要经过放入衣服、设置模式、定时、结束步骤,但不同的衣服设置的模式不同,可以抽象一个类,类中包含放入衣服、设置模式、定时、结束方法,其中设置模式的方法为抽象的,还有一个开始洗衣服的方法,这个方法中将按照放入衣服、设置模式、定时、结束步骤进行调用方法
package com.xiaoxu.principle.template;
public abstract class Wash {
public String close;
public Wash(String close) {
this.close = close;
}
// 洗的方式
public abstract void washMode();
// 烘干
public void dry() {
System.out.println("开始烘干衣服");
}
// 定时
public void timing() {
System.out.println("开始定时");
}
// 放入衣服
public void add() {
System.out.println("放入衣服");
}
// 结束洗衣服
public void stop() {
System.out.println("结束");
}
public final void start() {
System.out.println("------" + this.close + "------");
add();
washMode();
timing();
stop();
}
}
package com.xiaoxu.principle.template;
// 洗小衣服
public class WashBig extends Wash {
public WashBig(String close) {
super(close);
}
@Override
public void washMode() {
System.out.println("设置为大衣服模式");
}
}
package com.xiaoxu.principle.template;
// 洗小衣服
public class WashSmall extends Wash {
public WashSmall(String close) {
super(close);
}
@Override
public void washMode() {
System.out.println("设置为小衣服模式");
}
}
package com.xiaoxu.principle.template;
public class Main {
public static void main(String[] args) {
Wash wash = new WashBig("羽绒服");
wash.start();
Wash wash2 = new WashSmall("内裤");
wash2.start();
}
}
钩子方法
模板方法的父类中可以有一些什么都不干的方法,子类视情况选择进行覆盖,这些方法称为钩子方法,如果有的方法子类用不到,可以进行空实现
命令模式
需要向某些对象发送请求,指定发送者和接收者,可以消除发送者和接收者之间的耦合,命令模式可以撤销命令
- 调用者
- 命令接口:所有要执行的命令都在这个接口中,可以是接口也可以是抽象类
- 接收者:知道如何实施和执行相关的操作
- 实现类:将一个接收者对象与一个动作绑定,调用接收者相应的操作实现执行的效果
例子:电视、电脑的开关
代码:
package com.xiaoxu.principle.command;
public interface Command {
// 定义命令的执行方法
void execute();
}
package com.xiaoxu.principle.command;
public class ComputerTurnOff implements Command{
private ComputerReceiver computerReceiver;
public ComputerTurnOff(ComputerReceiver computerReceiver) {
this.computerReceiver = computerReceiver;
}
@Override
public void execute() {
System.out.println("电脑-准备执行:");
computerReceiver.off();
}
}
package com.xiaoxu.principle.command;
public class ComputerTurnOn implements Command{
private ComputerReceiver computerReceiver;
public ComputerTurnOn(ComputerReceiver computerReceiver) {
this.computerReceiver = computerReceiver;
}
@Override
public void execute() {
System.out.println("电脑-准备执行:");
computerReceiver.on();
}
}
package com.xiaoxu.principle.command;
public class TvTurnOff implements Command{
private TvReceiver tvReceiver;
public TvTurnOff(TvReceiver tvReceiver) {
this.tvReceiver = tvReceiver;
}
@Override
public void execute() {
System.out.println("电视-准备执行:");
tvReceiver.off();
}
}
package com.xiaoxu.principle.command;
public class TvTurnOn implements Command{
private TvReceiver tvReceiver;
public TvTurnOn(TvReceiver tvReceiver) {
this.tvReceiver = tvReceiver;
}
@Override
public void execute() {
System.out.println("电视-准备执行:");
tvReceiver.on();
}
}
package com.xiaoxu.principle.command;
public class TvReceiver {
public void on() {
System.out.println("电视开机");
}
public void off() {
System.out.println("电视已关闭");
}
}
package com.xiaoxu.principle.command;
public class ComputerReceiver {
public void on() {
System.out.println("电脑开机");
}
public void off() {
System.out.println("电脑已关闭");
}
}
package com.xiaoxu.principle.command;
import java.util.ArrayList;
import java.util.List;
public class Invoker {
private List<Command> commands = new ArrayList<>();
public void addCommands(Command command) {
commands.add(command);
}
public void clearCommands() {
commands.clear();
}
public void executeCommand() {
commands.forEach(Command::execute);
}
}
package com.xiaoxu.principle.command;
public class Main {
public static void main(String[] args) {
// 实例化一个调用者
Invoker invoker = new Invoker();
// 实例电脑开机命令
ComputerReceiver computerReceiver = new ComputerReceiver();
Command computerTurnOn = new ComputerTurnOn(computerReceiver);
// 实例化电视开机命令
TvReceiver tvReceiver = new TvReceiver();
Command tvTurnOn = new TvTurnOn(tvReceiver);
// 添加命令
invoker.addCommands(computerTurnOn);
invoker.addCommands(tvTurnOn);
// 执行命令
invoker.executeCommand();
// 实例化电脑关机、电视关机命令
Command computerTurnOff = new ComputerTurnOff(computerReceiver);
Command tvTurnOff = new TvTurnOff(tvReceiver);
// 清空命令
invoker.clearCommands();
// 添加命令
invoker.addCommands(computerTurnOff);
invoker.addCommands(tvTurnOff);
// 执行命令
invoker.executeCommand();
}
}
存在着类爆炸的问题
访问者模式
访问者模式visitor pattern
,封装一些作用于数据结构的各元素操作,可以在不改变数据结构的前提下定义这些元素新的操作
主要解决数据结构和操作的耦合性问题
工作原理:在被访问的类中添加一个对外提供接待访问者的接口
应用场景:对一个结构中的对象进行很多不同的操作(这些操作之间没有关联),同时避免污染这个对象的类
UML
类图大致如下:
Visitor
是一个抽象的访问者,为实现类提供声明Visitor
的实现类是具体的访问者,是每个操作的具体实现- 数据结构类允许访问者访问元素,里边可以存放一个
List
集合,使用这个集合关联相关对象,提供增加、删除等方法 Element
定义了一个accept()
方法,可以用于接收访问者对象Element
的实现类是具体的元素,实现了accept()
方法
例子:
学校里会开家长会,在家长会上家长将会看到每个同学的成绩、听取老师的汇报工作完成情况;校领导会在家长会上做旁听,也会看到每个同学的成绩,会对学生含有不及格的科目作出评价,同时也会根据老师工作汇报中的工作完成情况作出评价
根据以上条件,可以:
- 抽象一个接口
Visitor
,接口中有两个方法,一个是对学生进行访问的方法,一个是对老师进行访问的方法 - 需要学生家长、校领导实现
Visitor
接口完成相应的方法 - 需要抽象一个人类,这个类中抽象一个
accept
方法,用来接受访问 - 需要学生、老师继承人类实现接受访问的方法,并分别提供成绩、汇报工作情况
- 还需要添加一个家长会的类,用来当数据结构类,类中提供一个
List<人类>
,并提供添加、删除、访问所有人的方法(这个方法需要指定访问者),以用来完成对老师、学生的访问
UML
类图:
package com.xiaoxu.principle.visitor;
public abstract class Persion {
public abstract void accept(Visitor visitor);
}
package com.xiaoxu.principle.visitor;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class Student extends Persion {
private Map<String, Integer> map = new HashMap<>();
private String name;
public Student(String name) {
Random random = new Random();
map.put("语文", random.nextInt(100));
map.put("数学", random.nextInt(100));
map.put("英语", random.nextInt(100));
this.name = name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public Map<String, Integer> getStudentGrade() {
System.out.println(name + "同学:");
System.out.println("科目 成绩");
System.out.println("---------");
map.forEach((k, v) -> System.out.printf("%s %2d\n", k, v));
return map;
}
}
package com.xiaoxu.principle.visitor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class Teacher extends Persion {
private int[] workload = new int[5];
private String name;
public Teacher(String name) {
Random random = new Random();
for (int i = 0; i < workload.length; i++) {
workload[i] = random.nextInt(100);
}
this.name = name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public int[] getWorkload() {
System.out.println(name + "老师的工作量:" + Arrays.toString(workload));
return workload;
}
}
package com.xiaoxu.principle.visitor;
public interface Visitor {
void visit(Student student);
void visit(Teacher teacher);
}
package com.xiaoxu.principle.visitor;
public class StudentParentVisitor implements Visitor{
@Override
public void visit(Student student) {
System.out.println("家长查看学生各科成绩:");
student.getStudentGrade();
System.out.println();
}
@Override
public void visit(Teacher teacher) {
System.out.println("家长查看老师工作完成情况:");
teacher.getWorkload();
System.out.println();
}
}
package com.xiaoxu.principle.visitor;
import java.util.Arrays;
import java.util.Map;
public class LeaderVisitor implements Visitor{
@Override
public void visit(Student student) {
System.out.println("校领导查看学生成绩:");
Map<String, Integer> studentGrade = student.getStudentGrade();
studentGrade.forEach((k, v) -> {
if (v < 60) {
System.out.println("这个同学的【" + k + "】不及格,需要加把劲!");
}
});
System.out.println();
}
@Override
public void visit(Teacher teacher) {
System.out.println("校领导查看老师的工作情况:");
int[] workload = teacher.getWorkload();
long count = Arrays.stream(workload).filter(it -> it > 50).count();
if (count < 3) {
System.out.println("这个老师的工作还需要加把劲,争取提高效率!");
} else {
System.out.println("这个老师的工作完成的还可以,继续加油!");
}
System.out.println();
}
}
package com.xiaoxu.principle.visitor;
import java.util.ArrayList;
import java.util.List;
// 充当数据结构ObjectStructure的作用
public class ParentsMeeting {
private List<Persion> list = new ArrayList<>();
public void addPersion(Persion persion) {
list.add(persion);
}
public void removeAll() {
list.clear();
}
public void startVisit(Visitor visitor) {
list.forEach(it -> it.accept(visitor));
}
}
package com.xiaoxu.principle.visitor;
public class Main {
public static void main(String[] args) {
ParentsMeeting parentsMeeting = new ParentsMeeting();
parentsMeeting.addPersion(new Student("王二麻"));
parentsMeeting.addPersion(new Student("张大胖"));
parentsMeeting.addPersion(new Teacher("张丽"));
parentsMeeting.addPersion(new Student("李二小"));
Visitor studentParentVisitor = new StudentParentVisitor();
parentsMeeting.startVisit(studentParentVisitor);
Visitor leaderVisitor = new LeaderVisitor();
parentsMeeting.startVisit(leaderVisitor);
}
}
优点:
- 符合单一职责、扩展性好
- 适用于数据结构相对稳定的系统
缺点:
- 具体的元素要想访问者公布细节,上例中具体的元素是学生和老师,在
Visitor
接口的方法中直接写了学生和老师,而不是直接写的Person
- 违反了依赖倒转原则,访问者依赖的是具体实现而不是抽象元素
迭代器模式
迭代器模式属于行为模式,可以不暴露元素内部结构的情况下进行遍历元素
有一个接口,这个接口是迭代器接口,提供了三个方法,还有一个迭代器的实现类,在自定义类中有创建迭代器的方法,用来返回一个迭代器
优点:
- 提供了统一的方法遍历对象
- 隐藏了底层实现细节
缺点:
- 每个类都需要一个迭代器,不太好管理
观察者模式
角色分为:
Subject
:用来注册、删除、添加观察者,通常是一个接口Observer
:具体的观察者,也为一个接口,通常提供更新的方法,接收subject
给出的通知
Subject
和Observer
是一对多的关系
UML
类图大致如下:
例子:天气预报更新时,会通知所有的订阅天气预报服务的人,假设有两个订阅者:一个用户和一个公司
- 天气预报类需要继承
Subject
- 用户和公司是两个
Observer
,需要实现Observer
接口
代码:
package com.xiaoxu.principle.observer;
public interface Observer {
void update(String msg);
}
package com.xiaoxu.principle.observer;
public class Company implements Observer{
@Override
public void update(String msg) {
System.out.println("公司收到天气更新,更新内容为:" + msg);
}
}
package com.xiaoxu.principle.observer;
public class User implements Observer{
@Override
public void update(String msg) {
System.out.println("用户收到天气更新,更新内容为:" + msg);
}
}
package com.xiaoxu.principle.observer;
public interface Subject {
void registerObserver(Observer observer);
void remove(Observer observer);
void notifyObservers();
}
package com.xiaoxu.principle.observer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Weather implements Subject {
// 温度 风力
private int temperature, windPower;
// 天气建议
private String advice;
@Override
public String toString() {
return "Weather{" +
"temperature=" + temperature +
", windPower=" + windPower +
", advice='" + advice + '\'' +
'}';
}
private List<Observer> observers = new LinkedList<>();
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void remove(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
System.out.println("开始通知所有的观察者更新天气情况:");
observers.forEach(it -> it.update(toString()));
System.out.println();
}
public void changeData(int temperature, int windPower, String advice) {
this.temperature = temperature;
this.windPower = temperature;
this.advice = advice;
notifyObservers();
}
}
package com.xiaoxu.principle.observer;
public class Main {
public static void main(String[] args) {
Weather weather = new Weather();
Observer company = new Company();
Observer user = new User();
weather.registerObserver(company);
weather.registerObserver(user);
weather.changeData(34, 3, "好天气");
weather.changeData(334, 23, "不太好天气");
weather.changeData(2434, 23, "坏天气");
}
}
开始通知所有的观察者更新天气情况:
公司收到天气更新,更新内容为:Weather{temperature=34, windPower=34, advice='好天气'}
用户收到天气更新,更新内容为:Weather{temperature=34, windPower=34, advice='好天气'}
开始通知所有的观察者更新天气情况:
公司收到天气更新,更新内容为:Weather{temperature=334, windPower=334, advice='不太好天气'}
用户收到天气更新,更新内容为:Weather{temperature=334, windPower=334, advice='不太好天气'}
开始通知所有的观察者更新天气情况:
公司收到天气更新,更新内容为:Weather{temperature=2434, windPower=2434, advice='坏天气'}
用户收到天气更新,更新内容为:Weather{temperature=2434, windPower=2434, advice='坏天气'}
当增加观察者时无需修改其他地方的代码,遵循开闭原则
中介者模式
当对象数量特别多时,各个对象之间相互联系时会造成特别混乱,当增加一个新的对象或者流程改变时代码的可扩展性和可维护性都不理想,这个时候可以考虑中介者模式
用一个中介对象来封装一系列的对象交互,中介者可以使各个对象之间不需要显式的相互调用,也就是说把类与类之间的耦合放到一个中间类中,从而使其解耦合,属于行为型模式
在MVC
模式中C
是M
和V
的中介者
备忘录模式
备忘录模式memento pattern
会在不破坏封装性的前提下,获取一个对象的内部状态并进行保存到对象之外,可以随时的恢复对象的状态,也是属于行为模式
将一个类中的部分属性值保存到另外一个类中,并且提供一个保存内容的管理的类,类中有一个集合维护保存内容的对象
代码:
package com.xiaoxu.principle.memento;
public class Entity {
// 用来表示某个属性,这个属性需要存储
private String attribute;
// 无关紧要的属性
public String variable;
public String getAttribute() {
return attribute;
}
public void setAttrbute(String attrbute) {
this.attribute = attrbute;
}
// 存储需要存储的内容
public Memento save() {
return new Memento(attribute);
}
// 恢复内容
public void recoveryFromMemento(Memento memento) {
this.attribute = memento.content;
}
}
package com.xiaoxu.principle.memento;
public class Memento {
public String content;
public Memento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
package com.xiaoxu.principle.memento;
import java.util.HashMap;
// 管理存储的内容
public class MementoMananger {
private HashMap<String, Memento> map = new HashMap<>();
public void save(String key, Memento m) {
map.put(key, m);
}
public Memento getMemento(String key) {
return map.get(key);
}
}
package com.xiaoxu.principle.memento;
// 备忘录模式
public class Main {
public static void main(String[] args) {
Entity entity = new Entity();
entity.setAttrbute("这是一个参数");
MementoMananger mementoMananger = new MementoMananger();
mementoMananger.save("first", entity.save());
entity.setAttrbute("改变参数");
mementoMananger.save("second", entity.save());
entity.setAttrbute("改变参数222");
entity.setAttrbute("改变参数333");
mementoMananger.save("third", entity.save());
// 恢复到第一次
entity.recoveryFromMemento(mementoMananger.getMemento("first"));
System.out.println("entity.getAttribute() = " + entity.getAttribute());
// 恢复到第三次
entity.recoveryFromMemento(mementoMananger.getMemento("third"));
System.out.println("entity.getAttribute() = " + entity.getAttribute());
// 恢复到第二次
entity.recoveryFromMemento(mementoMananger.getMemento("second"));
System.out.println("entity.getAttribute() = " + entity.getAttribute());
}
}
- 给用户提供了一个恢复机制,能够很方便的恢复到某个历史时刻的状态
- 可以和原型模式配合使用
- 如果需要保存成员变量过多,会比较zhan’yong
解释器模式
解释器模式(interpreter pattern
)inˈtərprədər
,给定一个语言或者表达式,定义语法的表示方式,定义一个解释器,使用解释器解释语言或者表达式
计算器的例子,先输入表达式,计算十以内的加减法:
a+b-c+d
请输入a:1
请输入b:2
请输入c:3
请输入d:4
calculator.run(map) = 4
代码:
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
public abstract class Expression {
public abstract int interpret(HashMap<String, Integer> var);
}
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
public class VariableExpression extends Expression {
private String key;
public VariableExpression(String key) {
this.key = key;
}
@Override
public int interpret(HashMap<String, Integer> var) {
return var.get(key);
}
}
package com.xiaoxu.principle.interpreter;
public abstract class OperationExpression extends Expression {
public Expression left, right;
public OperationExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
public class AddOperationExpression extends OperationExpression{
public AddOperationExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret(HashMap<String, Integer> var) {
return left.interpret(var) + right.interpret(var);
}
}
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
public class SubtractOperationExpression extends OperationExpression{
public SubtractOperationExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret(HashMap<String, Integer> var) {
return left.interpret(var) - right.interpret(var);
}
}
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
import java.util.Stack;
public class Calculator {
private Expression expression;
public Calculator(String expression) {
char[] chars = expression.toCharArray();
Stack<Expression> stack = new Stack<>();
for (int i = 0; i < chars.length; i++) {
switch (chars[i]) {
case '+':
Expression left = stack.pop();
Expression right = new VariableExpression(String.valueOf(chars[++i]));
stack.push(new AddOperationExpression(left, right));
break;
case '-':
Expression left2 = stack.pop();
Expression right2 = new VariableExpression(String.valueOf(chars[++i]));
stack.push(new SubtractOperationExpression(left2, right2));
break;
default:
stack.push(new VariableExpression(String.valueOf(chars[i])));
}
}
this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
return expression.interpret(var);
}
}
package com.xiaoxu.principle.interpreter;
import java.util.HashMap;
import java.util.Scanner;
// 解释器模式
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String exp = input.nextLine();
HashMap<String, Integer> map = new HashMap();
for (int i = 0; i < exp.length(); i++) {
char c = exp.charAt(i);
if (!(c == '+' || c == '-')) {
System.out.print("请输入" + c + ":");
map.put(String.valueOf(c), input.nextInt());
}
}
Calculator calculator = new Calculator(exp);
System.out.println("calculator.run(map) = " + calculator.run(map));
}
}
缺点:容易引起类爆炸、递归层数高等问题
状态模式
状态模式(state pattern
)解决对象在多种状态转换时需要对外输出不同行为的问题,状态和行为是一一对应的,可以相互转换,也就是在一个对象的内部可以改变这个对象所处的状态
-
有一个抽象类为狗类,子类分别为幼年狗、成年狗、老年狗,抽象狗类提供一个抽象的成长方法,三个子类分别进行实现
-
还有一个活动类,用于记录当前的状态,活动类中通过抽象的狗类进行聚合这三种年龄的狗,狗类中可以聚合活动类,构成关联关系
-
幼年狗类实现成长方法时可以将活动类中当前状态设置为成年狗
-
这就体现出了在一个对象的内部可以改变这个对象所处的状态
-
class 抽象狗类 { 活动类 成员变量; 抽象方法成长(); } class 幼年狗 extends 抽象狗类 { 成长() { 活动类.设置狗的状态(成年狗); } } class 活动类 { 抽象狗类 狗; 设置狗的类型(抽象狗类 狗) { this.狗 = 狗; } }
UML
类图:
例子:一个软件的生命周期包含:创建、开始、销毁,生命周期由activity
进行管理,默认的状态为创建,当执行完创建时,将生命周期改变为开始,当执行完开始时,生命周期改编为销毁
package com.xiaoxu.principle.state;
public abstract class Lifecycle {
protected Activity activity;
public Lifecycle(Activity activity) {
this.activity = activity;
}
// 生命周期的回调
public abstract void callback();
}
package com.xiaoxu.principle.state;
public class Create extends Lifecycle{
public Create(Activity activity) {
super(activity);
}
@Override
public void callback() {
System.out.println("开始创建应用程序...");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
activity.setStatus(activity.getStart());
}
}
package com.xiaoxu.principle.state;
public class Start extends Lifecycle{
public Start(Activity activity) {
super(activity);
}
@Override
public void callback() {
System.out.println("开始执行程序...");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
activity.setStatus(activity.getDestroy());
}
}
package com.xiaoxu.principle.state;
public class Destroy extends Lifecycle{
private boolean isDestroyed = false;
public Destroy(Activity activity) {
super(activity);
}
@Override
public void callback() {
if (isDestroyed) {
throw new RuntimeException("执行销毁回调失败,已经销毁了");
}
System.out.println("执行destroy的回调");
isDestroyed = true;
}
}
package com.xiaoxu.principle.state;
public class Activity {
private Create create = new Create(this);
private Start start = new Start(this);
private Destroy destroy = new Destroy(this);
private Lifecycle status;
public Activity() {
this.status = create;
}
public void setStatus(Lifecycle status) {
this.status = status;
}
public Create getCreate() {
return create;
}
public Start getStart() {
return start;
}
public Destroy getDestroy() {
return destroy;
}
public Lifecycle getStatus() {
return status;
}
}
package com.xiaoxu.principle.state;
public class Main {
public static void main(String[] args) {
Activity activity = new Activity();
activity.getStatus().callback();
activity.getStatus().callback();
activity.getStatus().callback();
activity.getStatus().callback();
}
}
优点:
- 消除了部分
if - else if - ... - else
- 符合开闭原则
缺点:
- 会产生很多类
当一个对象或者一个事件有很多状态时,适合使用状态模式
策略模式
策略模式(strategy pattern
),strategy 中文为策略、战略,读音ˈstrætədʒi
,定义算法族,分别封装起来,使他们可以相互替换,让算法的变化独立于使用算法的客户
体现出了针对接口编程、多用组合/聚合少用继承
可以在构造器中指定具体的策略
例子:鸭子有不同的种类,不同种类的鸭子叫声、游泳方式、飞行方式不同
最简单方式:
class 鸭子类 {
飞行方法();
游泳方法();
叫的方法();
}
class 北京鸭子 extends 鸭子类 {
根据需要重写覆盖相关方法
}
class 玩具鸭子 extends 鸭子类 {
根据需要重写覆盖相关方法
}
例如玩具鸭子不会飞,那么此时只能空实现鸭子类的飞行方法
可以使用策略模式解决,把每个动作都抽象为一个接口,例如飞行可以抽象为一个接口,最后创建具体的鸭子类进行实现,使用这个接口聚合相关的实现类即可
package com.xiaoxu.principle.strategy;
public interface Fly {
void fly();
}
package com.xiaoxu.principle.strategy;
public class NoFly implements Fly{
@Override
public void fly() {
System.out.println("这个鸭鸭不会飞");
}
}
package com.xiaoxu.principle.strategy;
public class CanFly implements Fly{
@Override
public void fly() {
System.out.println("这个鸭鸭会飞");
}
}
package com.xiaoxu.principle.strategy;
public class Duck {
private Fly fly;
public Duck(Fly fly) {
this.fly = fly;
}
public void fly() {
System.out.print("普通鸭子,");
fly.fly();
}
}
package com.xiaoxu.principle.strategy;
public class ToyDuck {
private Fly fly;
public ToyDuck(Fly fly) {
this.fly = fly;
}
public void fly() {
System.out.print("玩具鸭子,");
fly.fly();
}
}
Arrays.sort(数组, 排序接口)
就是使用了策略模式
缺点:
- 每增加一个策略就相当于增加一个类,类的数量比较多
职责链模式
职责链模式(chain of responsibility pattern
)又被称为责任链模式,为请求创建了一个接收者的链,这种模式对请求者和处理者进行解耦,属于行为型模式。
一个请求者对应着多个处理者,当一个请求者发出请求时首相交给一个处理者进行处理,当一个处理者无法处理时,直接转交给下个处理者,如果下个处理者仍然无法处理,那么继续往下。
让多个对象都有机会处理请求,避免了if - else if - ... - else
的耦合关系,将这个对象构成一条链,沿着这条链处理请求,直到有一个能够处理它的对象为止
抽象类定义了处理者的基本结构,实现类是具体的请求处理者,Request
就是一个请求
例子:采购员采购器材
- 器材金额小于等于3000,主任来审批
- 器材金额小于等于5000,书记来审批
- 器材金额小于等于7000,院长来审批
- 器材金额小于等于9000,副校长来审批
- 器材金额大于9000,校长来审批
在传统的解决方案中,针对以上问题将使用:
if (金额 <= 3000) {
主任审批;
} else if (金额 <= 5000) {
书记审批;
}
........
else {
校长来审批;
}
使用职责链模式可以定义一个抽象处理的类,类中把处理方法写为抽象的,并且提供一个同类型的next
成员变量,使得主任、书记、院长、副校长、校长都继承这个抽象类并实现处理方法,在各个角色中如果无法处理,那么就直接交给下个角色进行处理,再写一个Request
类用来表示请求的类
package com.xiaoxu.principle.chain;
public class Request {
public String name;
public int price;
public Request(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Request{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
package com.xiaoxu.principle.chain;
public abstract class Handler {
protected Handler next;
public abstract void handle(Request request);
public Handler(Handler next) {
this.next = next;
}
}
package com.xiaoxu.principle.chain;
// 主任审批
public class DirectorHandler extends Handler {
public DirectorHandler(Handler next) {
super(next);
}
@Override
public void handle(Request request) {
if (request.price <= 3000) {
System.out.println("由主任审批");
return;
}
System.out.print("主任交给下一个领导审批->");
next.handle(request);
}
}
package com.xiaoxu.principle.chain;
// 书记审批
public class SecretaryHandler extends Handler {
public SecretaryHandler(Handler next) {
super(next);
}
@Override
public void handle(Request request) {
if (request.price <= 5000) {
System.out.println("由书记审批");
return;
}
System.out.print("书记交给下一个领导审批->");
next.handle(request);
}
}
package com.xiaoxu.principle.chain;
// 院长审批
public class DeanHandler extends Handler {
public DeanHandler(Handler next) {
super(next);
}
@Override
public void handle(Request request) {
if (request.price <= 7000) {
System.out.println("由院长审批");
return;
}
System.out.print("院长交给下一个领导审批->");
next.handle(request);
}
}
package com.xiaoxu.principle.chain;
// 副校长审批
public class VicePrincipalHandler extends Handler {
public VicePrincipalHandler(Handler next) {
super(next);
}
@Override
public void handle(Request request) {
if (request.price <= 9000) {
System.out.println("由副校长审批");
return;
}
System.out.print("副校长交给下一个领导审批->");
next.handle(request);
}
}
package com.xiaoxu.principle.chain;
// 校长审批
public class PrincipalHandler extends Handler {
public PrincipalHandler(Handler next) {
super(next);
}
@Override
public void handle(Request request) {
System.out.println("校长审批");
}
}
优点:
- 将请求和处理分开,实现了解耦
- 对象不需要关注链的结构
缺点:
- 性能可能受到影响,因此可能需要手动的控制链的长度
- 调试不方便
Q.E.D.