文章目录
大部分代码来自菜鸟教程,这是学习笔记,详细学习可到菜鸟教程-设计模式,或购买书籍。
设计模式的目的
- 代码可用性
- 可读性
- 可扩展性
- 可靠性
- 使程序呈现高内聚、低耦合
设计模式的七大原则
就是在编程时,需要遵循的原则
1. 单一职责原则
基本介绍
降低类的复杂度,一个类只负责一项职责。
2. 接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
3. 依赖倒转原则
- 高层模块不应该依赖于底层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒置的中心思想是面向接口编程。
- 抽象指的是接口或抽象类,细节就是具体的实现类。
- 使用接口或抽象类的目的就是制定好规范,而不涉及任何具体的操作。
4. 里氏替换原则
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类方法。
5. 开闭原则
- 开闭原则是编程中最基础、最重要的设计原则。
- 一个软件实体,如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。(就是在修改业务代码后,使用的代码不用进行修改)
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。
- 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
6. 迪米特原则
- 一个对象应该对其他对象保持最少的了解。
- 类与类的关系越密切,耦合度就越大。
- 对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露信息。
- 陌生的类最好不要以局部变量的形式出现在类的内部。
7. 合成复用原则
尽量使用合成(set)/聚合(new一个成员变量)的方式,而不是使用继承。(就是使用new成员变量的方式或者使用set)
类之间的关系
依赖关系
只要在类中使用到了对方,两个类之间就存在依赖关系。
泛化关系
泛化关系实际上就是继承关系,它是依赖关系的特例。
实现关系
实现关系实际上就是A类实现B接口,它是依赖关系的特例。
关联关系
- 关联关系就是类与类之间的联系,它是依赖关系的特例。
- 可以是双向关系或单向关系。(一对一、一对多、多对多)
聚合关系
- 实体类之间可以分离的就是聚合关系(比如电脑类中包含一个鼠标类和键盘类,但是他们可以分离的)
- 聚合关系表示的是整体和部分的关系,整体和部分可以分开。聚合关系是关联关系的特例。
组合关系
实体类之间不能进行分离,如在Person类中IdCard idCard = new IdCard();
,这样Idcard类就和Person同时创建、销毁。
设计模式的分类
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
- 结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式
(1)单例模式
一、饿汉式
- 优点:基于classloader机制避免了多线程的同步问题。线程安全的。
- 缺点:类加载时就初始化,浪费内存。
/**
* 饿汉式
* 优点:线程安全
* 缺点:类加载就初始化,浪费内存
*/
public class Single01{
private static Single01 single = new Single01();
// 私有化构造器
private Single01() {
}
// 提供一个外界的访问方法
public static Single01 getInstance() {
return single;
}
}
二、懒汉式:线程不安全的
- 优点:实现懒加载。
/**
* 懒汉式:线程不安全的
* 优点:实现了懒加载
*/
public class Single02 {
private static Single02 single;
public Single02() {}
public static Single02 getInstance() {
if(single == null)
single = new Single02();
return single;
}
}
1. 模拟多线程,进行测试
- 使用
Thread.sleep(200)
,增大获取不同实例的机会。
/**
* 懒汉式:线程不安全的
* 优点:实现了懒加载
*/
public class Single02 {
private static Single02 single;
public Single02() {}
public static Single02 getInstance() {
// 多线程环境下
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(single == null)
single = new Single02();
return single;
}
}
- 模拟多线程
public class Test02 {
public static void main(String[] args) {
// 模拟多线程测试
new Thread("t1") {
@Override
public void run() {
Single02 single1 = Single02.getInstance();
// Single02@10f33626
System.out.println(single1);
}
}.start();
new Thread("t2") {
@Override
public void run() {
Single02 single2 = Single02.getInstance();
// Single02@25c8063f
System.out.println(single2);
}
}.start();
}
}
- 获取实例不同。
三、懒汉式:线程安全
- 加入
synchronized
同步代码块,保证线程安全。 - 但是如果频繁调用getInstance()方法,效率低。
/**
* 懒汉式:线程安全
*/
public class Single03 {
private static Single03 single;
private Single03() {}
public static Single03 getInstance() {
// 加锁
synchronized (Single03.class) {
if(single == null)
single = new Single03();
return single;
}
}
}
四:懒汉式:双重检查锁(double-cheched-locking)
- 双重锁,在多线程情况下能保持高性能。
/**
* 双重检查锁
*/
public class Single04 {
private static Single04 single;
private Single04() {}
public static Single04 getInstance() {
if(single == null) {
synchronized (Single04.class) {
if(single == null) {
single = new Single04();
}
}
}
return single;
}
}
1. 方式二:将对象声明为volatile
- 将对象声明为volatitle后,重排序在多线程环境中将会被禁止。
/**
* 双重检查锁
*/
public class Single04 {
private static volatitle Single04 single;
private Single04() {}
public static Single04 getInstance() {
if(single == null) {
synchronized (Single04.class) {
if(single == null) {
single = new Single04();
}
}
}
return single;
}
}
2. 使用反射和序列化的方式破坏单例模式
- 私有化构造器的都可以通过反射对单例模式进行破坏。
public class Test01 {
public static void main(String[] args) {
// 测试饿汉式
Single01 single1 = Single01.getInstance();
Single01 single2 = Single01.getInstance();
// true
System.out.println(single1 == single2);
// 通过反射破除单例模式
try {
Class<Single01> aClass = Single01.class;
Constructor<Single01> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
// 通过反射回去一个实例
Single01 single01 = constructor.newInstance();
// false
System.out.println(single01 == single2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 对象进行序列化,反序列化的时候,会创建一个新的对象。
public class Test01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 测试饿汉式
Single01 single1 = Single01.getInstance();
Single01 single2 = Single01.getInstance();
// true
System.out.println(single1 == single2);
// 通过反序列化破解单例:要实现Serializable接口
// 通过反序列化把对象写出到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/test.txt"));
oos.writeObject(single1);
// 写出完成刷新
oos.flush();
oos.close();
// 将序列化后的对象写回内存
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/test.txt"));
// 获得序列化后的对象
Single01 obj = (Single01) ois.readObject();
// false
System.out.println(single1 == obj);
}
}
五、静态内部类
- 静态内部类,在外部被装载的时候,single对象不一定被初始化。
- 因为SingleHolder没有被主动调用。只有在调用getInstance()方法时,才会显式的调用SingleHolder类,从而实例化single。
/**
* 静态内部类
*/
public class Single05 {
private Single05() {}
// 使用静态内部类
private static class SingleHolder {
private static Single05 single = new Single05();
}
public static Single05 getInstance() {
return SingleHolder.single;
}
}
六、枚举
- 实现单例模式的最佳方式。
- 避免了多线程同步的问题。
- 自动支持序列化机制,防止反序列化重新创建新的对象。
/**
* 使用枚举的方式
*/
public enum Single06 {
INSTANCE;
public void show() {
System.out.println("show");
}
}
(2)工厂模式
- 让其子类实现接口,返回一个具体的产品。
第一步:创建一个接口
/**
* 形状接口
*/
public interface Shape {
void draw();
}
第二步:创建接口的实现类
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("circle");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("square");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("rectangle");
}
}
第三步:创建工厂类
/**
* 工厂类
*/
public class ShapeFactory {
public static Shape getShape(String shapeName) {
if(shapeName == null)
return null;
if("circle".equals(shapeName)) {
return new Circle();
} else if ("square".equals(shapeName)) {
return new Square();
} else if ("rectangle".equals(shapeName)) {
return new Rectangle();
}
return null;
}
}
第四步:创建Demo进行测试
public class FactoryDemo {
public static void main(String[] args) {
// 使用工厂获取具体的类型
Shape circle = ShapeFactory.getShape("circle");
circle.draw();
}
}
(3) 抽象工厂模式
- 抽象工厂模式(Abstract Factory Pattern),抽象工厂可以生产一个产品族,比如一个工厂可以生产手机和耳机。
第一步:创建手机和耳机的抽象类
/**
* 手机抽象类
*/
public abstract class Phone {
abstract void name();
}
/**
* 耳机抽象类
*/
public abstract class HeadSet {
abstract void play();
}
第二步:创建华为手机和小米手机
/**
* 华为手机
*/
public class HuaweiPhone extends Phone{
@Override
void name() {
System.out.println("华为手机");
}
}
/**
* 小米手机
*/
public class XiaoMiPhone extends Phone{
@Override
void name() {
System.out.println("小米手机");
}
}
第三步:创建华为耳机和小米耳机
/**
* 华为耳机
*/
public class HuaweiHeadSet extends HeadSet{
@Override
void play() {
System.out.println("华为耳机:播放");
}
}
/**
* 小米耳机
*/
public class XiaoMiHeadSet extends HeadSet{
@Override
void play() {
System.out.println("小米耳机:播放");
}
}
第四步:创建一个抽象工厂,能够生产产品族手机和耳机
/**
* 抽象工厂:创建产品族
*/
public interface AbstractFactory{
Phone getPhone();
HeadSet getHeadSet();
}
第五步:创建华为工厂和小米工厂,实现抽象工厂
/**
* 华为工厂创建华为的产品组
*/
public class HuaWeiFactory implements AbstractFactory{
@Override
public Phone getPhone() {
return new HuaweiPhone();
}
@Override
public HeadSet getHeadSet() {
return new HuaweiHeadSet();
}
}
/**
* 小米工厂:生成小米产品组
*/
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone getPhone() {
return new XiaoMiPhone();
}
@Override
public HeadSet getHeadSet() {
return new XiaoMiHeadSet();
}
}
第六步:创建Demo类进行测试
public class Demo {
public static void main(String[] args) {
// 创建一个华为工厂
AbstractFactory huaWeiFactory = new HuaWeiFactory();
Phone huaWeiPhone = huaWeiFactory.getPhone();
HeadSet huaWeiHeadSet = huaWeiFactory.getHeadSet();
huaWeiPhone.name();
huaWeiHeadSet.play();
}
}
(4)建造者模式
- 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂对象。
- 适用于:一些基础部件不会经常改变,而其组合经常变化时。
- 实现案例:我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
第一步:创建一个商品项接口和食物包装接口
/**
* 商品项接口
*/
public interface Item {
String name();
// 包装
Packing pacing();
float price();
}
/**
* 包装接口
*/
public interface Packing {
String pack();
}
第二步:创建Packing包装接口的实现类
/**
* 包装纸:实现包装接口
*/
public class Wrapper implements Packing {
@Override
public String pack() {
return "wrapper";
}
}
/**
* 包装瓶:实现包装接口
*/
public class Bottle implements Packing {
@Override
public String pack() {
return "bottle";
}
}
第三步:创建Item商品接口的抽象类Burger和ColdDrink。
/**
* 汉堡类抽象类:实现商品接口
*/
public abstract class Burger implements Item{
// 汉堡使用包装纸进行包装
@Override
public Packing pacing() {
return new Wrapper();
}
}
/**
* 冷饮抽象类:实现item接口
*/
public abstract class ColdDrink implements Item{
// 冷饮使用瓶子包装
@Override
public Packing pacing() {
return new Bottle();
}
}
第四步:创建抽象类Burger和ColdDrink的实现类
/**
* 蔬菜汉堡:继承汉堡类
*/
public class VegBurger extends Burger {
@Override
public String name() {
return "vegBurger";
}
@Override
public float price() {
return 25.0f;
}
}
/**
* 鸡肉汉堡:继承汉堡类
*/
public class ChickenBurger extends Burger{
@Override
public String name() {
return "chickenBurger";
}
@Override
public float price() {
return 35.0f;
}
}
/**
* 可口可乐:继承可乐类
*/
public class Coke extends ColdDrink {
@Override
public String name() {
return "cock";
}
@Override
public float price() {
return 10.0f;
}
}
/**
* 百事可乐:继承冷饮类
*/
public class Pepsi extends ColdDrink{
@Override
public String name() {
return "pepsi";
}
@Override
public float price() {
return 15.0f;
}
}
第五步:创建Meal商品类
/**
* 一顿餐的组合
*/
public class Meal {
private List<Item> items = new ArrayList<>();
// 添加组合
public void addItem(Item item) {
items.add(item);
}
// 计算商品总价
public float getCost() {
float cost = 0.0f;
for(Item item : items) {
cost += item.price();
}
return cost;
}
// 查看一餐的所有信息
public void showItems() {
for(Item item : items) {
System.out.println("商品名:" + item.name());
System.out.println("商品包装:" + item.pacing().pack());
System.out.println("商品总价:" + item.price());
}
}
}
第六步:创建MealBuilder类,实际的创建者builder类负责创建Meal对象。
/**
* 实际的builder类,用于创建Meal对象
*/
public class MealBuilder {
// 创建一个蔬菜汉堡套餐
public Meal prepareVegMeal() {
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
// 创建一个鸡肉汉堡套餐
public Meal prepareChickenMeal() {
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
第七步:Demo进行测试
public class MealDemo {
public static void main(String[] args) {
// 获取创建者
MealBuilder builder = new MealBuilder();
// 使用创建者创建一个套餐
Meal chickenMeal = builder.prepareChickenMeal();
chickenMeal.showItems();
float cost = chickenMeal.getCost();
System.out.println(cost);
}
}
(5)原型模式
- 在系统中需要创建大量相同或相似的对象。
- 核心思想:通过拷贝指定的原型对象,创建和该对象一样的新对象。(克隆指定对象)
- 需要拷贝的原型类必须实现
Cloneable
接口(该接口没有任何内容,一个标记接口,给实现类一个实现标记),重写Object
类中的clone
方法。(只有当一个类实现Cloneable接口过后,才能调用重写自Object中的clone方法,否则会抛出CloneNotSupportedException
异常)
1. 浅拷贝
- 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值给新对象。
- 当成员变量时引入数据类型时,浅拷贝复制的是引用数据类型的地址。这种情况下,当拷贝出来的类修改引用类型的成员变量,会导致所有拷贝的类发生变化。
/**
* 原型模式:克隆要实现Cloneable接口,重写Object中的clone方法
*/
public class Person implements Cloneable{
private String name;
private String job;
public Person(String name, String job) {
this.name = name;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
// 重写Object中的clone方法
@Override
protected Object clone() {
Person person = null;
try {
// 克隆一个类
person = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
测试
public class Demo {
public static void main(String[] args) {
// 新建一个Person类
Person person = new Person("小张", "程序员");
// 克隆一个Person对象
Person clonePerson = (Person) person.clone();
clonePerson.setName("小王");
System.out.println(person);
System.out.println(clonePerson);
// Person{name='小张', job='程序员'}
// Person{name='小王', job='程序员'}
}
}
2. 深拷贝
- 在Person类中添加一个引用数据类型Computer(也需要重写Cloneable接口)。
public class Computer implements Cloneable {
private String color;
public Conputer(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Computer{" +
"color='" + color + '\'' +
'}';
}
// 重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
* 原型模式:克隆要实现Cloneable接口,重写Object中的clone方法
*/
public class Person implements Cloneable {
private String name;
private String job;
private Computer computer;
public Person(String name, String job, Computer computer) {
this.name = name;
this.job = job;
this.computer = computer;
}
// ...get、set方法
@Override
protected Object clone() {
Person person = null;
try {
// 克隆一个对象
person = (Person) super.clone();
// 深拷贝引用类型
person.computer = (Computer) this.computer.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
// ...toString()
}
测试:
public class Demo {
public static void main(String[] args) {
// 新建一个Person类
Person person = new Person("小张", "程序员", new Computer("黑色"));
// 克隆一个Person对象
Person clonePerson = (Person) person.clone();
clonePerson.setComputer(new Computer("白色"));
System.out.println(person);
System.out.println(clonePerson);
}
}
该方法有一个缺陷:需要单独处理所有要克隆的类中的引用类型。
序列化实现深拷贝(推荐)
- Person类和Computer类实现序列化接口
Serializable
接口,Computer类加入一个序列化ID。 - 使用序列化和反序列的方式,实现深拷贝。
public class Person implements Serializable {
// 使用序列化的方式:对Person对象进行深拷贝
public Person deepClone() {
ByteArrayOutputStream bos = null;
ByteArrayInputStream bis = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
// 创建字节数组流
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
// 序列化对象
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
// 反序列化对象
return (Person) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
// 关闭流
if(bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(oos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ois != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
测试:
public class Demo {
public static void main(String[] args) {
// 新建一个Person类
Person person = new Person("小张", "程序员", new Computer("黑色"));
// 使用序列化的方式,进行深拷贝
Person clonePerson = person.deepClone();
clonePerson.setName("小李");
clonePerson.getComputer().setColor("白色");
System.out.println(person);
System.out.println(clonePerson);
}
}
(6)适配器模式
- 适配器模式(Adapter pattern)是作为两个不相同接口之间的桥梁。
- 将一个类的接口转化为一个接口。
- 播放器
AudioPlayer
默认只能播放mp3类型。需要加入一个播放的适配器类,用户播放其他类型。
第一步:创建一个媒体播放接口MediaPlayer
/**
* 播放器:默认只能播放mp3
*/
public interface MediaPlayer {
void play(String audioType, String filename);
}
第二步:创建一个扩展媒体的播放接口AdvanceMediaPlayer
,用于播放其他类型
/**
* 扩展播放的接口:可以播放其他音频或视频
*/
public interface AdvanceMediaPlayer {
void playOther(String audioType, String filename);
}
第三步:创建扩展媒体播放器的实现类Mp4Player
。
/**
* MP4的播放器,实现扩展播放的接口
*/
public class Mp4Player implements AdvanceMediaPlayer {
@Override
public void playOther(String audioType, String filename) {
System.out.println("播放" + audioType + ":" + filename);
}
}
第四步:创建适配器类MediaAdapter
,用于播放其他类型
/**
* 播放的适配器
*/
public class MediaAdapter implements MediaPlayer{
// 加入一个扩展播放器
private AdvanceMediaPlayer advanceMediaPlayer;
@Override
public void play(String audioType, String filename) {
if(audioType.equals("mp4")) {
// 如果是播放其他就播放其他
advanceMediaPlayer = new Mp4Player();
advanceMediaPlayer.playOther("mp4", filename);
}
}
}
第五步:创建播放器类AudioPlayer
/**
* 音频播放器
*/
public class AudioPlayer implements MediaPlayer{
// 加入一个适配器
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String filename) {
// 默认播放mp3
if(audioType.equals("mp3")) {
System.out.println("播放mp3:" + filename);
} else if (audioType.equals("mp4")) {
// 播放其他就使用一个适配器进行播放
mediaAdapter = new MediaAdapter();
mediaAdapter.play("mp4", filename);
}
}
}
(7)桥接模式
- 桥接(Bridge)是用于把抽象化与实现化解耦。它提供抽象化和实现化之间的桥接结构,来实现二者解耦。
- 一个桥接接口,使得实体类的功能能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
第一步:新建一个桥接接口DrawAPI
/**
* 创建一个桥接接口:用于画各种不同颜色的圆
*/
public interface DrawAPI {
void drawCircle(int radius, int x, int y);
}
第二步:创建桥接接口的实现类RedCircle
和BlueCircle
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("drawing red circle [radius: " + radius + ",x: " + x + ",y: " + y + "]");
}
}
public class BlueCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("drawing blue circle [radius: " + radius + ",x: " + x + ",y: " + y + "]");
}
}
第三步:创建抽象的形状Shape
类
/**
* 创建一个抽象的形状类
*/
public abstract class Shape {
DrawAPI drawAPI;
public Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
public abstract void draw();
}
第四步:创建具体的形状类Circle
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
drawAPI.drawCircle(radius, x, y);
}
}
第五步:创建Demo
public class Demo {
public static void main(String[] args) {
Shape redCircle = new Circle(2,2,4, new RedCircle());
Shape blueCircle = new Circle(7,7,5, new BlueCircle());
redCircle.draw();
blueCircle.draw();
}
}
(8)过滤器模式
- 过滤器模式(Filter Pattern)或标准模式(Criteria Pattern),允许开发人员使用不同的标准来过滤一组对象。
第一步:创建需要过滤的类Person
/**
* 过滤的对象
*/
public class Person {
private String name;
private String gender;
// 婚姻状况
private String maritalStatus;
public Person() {
}
public Person(String name, String gender, String maritalStatus) {
this.name = name;
this.gender = gender;
this.maritalStatus = maritalStatus;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getMaritalStatus() {
return maritalStatus;
}
public void setMaritalStatus(String maritalStatus) {
this.maritalStatus = maritalStatus;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", maritalStatus='" + maritalStatus + '\'' +
'}';
}
}
第二步:创建一个过滤接口Criteria
/**
* 创建过滤的接口
*/
public interface Criteria {
List<Person> filter(List<Person> persons);
}
第三步:创建过滤接口的实现类:过滤男CriteriaMale
、女CriteriaFemale
和单身CriteriaSingle
/**
* 男过滤器
*/
public class CriteriaMale implements Criteria {
@Override
public List<Person> filter(List<Person> persons) {
List<Person> maleList = new ArrayList<>();
// 遍历进行过滤
for(Person p : persons) {
String gender = p.getGender();
if(gender.equals("male")) {
maleList.add(p);
}
}
return maleList;
}
}
/**
* 女过滤器
*/
public class CriteriaFemale implements Criteria {
@Override
public List<Person> filter(List<Person> persons) {
List<Person> femaleList = new ArrayList<>();
for(Person p : persons) {
String gender = p.getGender();
if(gender.equals("female")) {
femaleList.add(p);
}
}
return femaleList;
}
}
/**
* 单身过滤器
*/
public class CriteriaSingle implements Criteria{
@Override
public List<Person> filter(List<Person> persons) {
List<Person> personList = new ArrayList<>();
for(Person p : persons) {
if(p.getMaritalStatus().equals("single")) {
personList.add(p);
}
}
return personList;
}
}
第四步:创建组合过滤器AndCriteria
、或过滤器OrCriteria
/**
* 组合过滤器
*/
public class AndCriteria implements Criteria {
// 使用两个过滤器,进行组合过滤
private Criteria criteria;
private Criteria otherCriteria;
public AndCriteria(Criteria criteria, Criteria otherCriteria) {
this.criteria = criteria;
this.otherCriteria = otherCriteria;
}
@Override
public List<Person> filter(List<Person> persons) {
// 先进行过滤
List<Person> list = criteria.filter(persons);
// 在进行第二次过滤
return otherCriteria.filter(list);
}
}
/**
* 相当于 or
*/
public class OrCriteria implements Criteria{
private Criteria criteria;
private Criteria otherCriteria;
public OrCriteria(Criteria criteria, Criteria otherCriteria) {
this.criteria = criteria;
this.otherCriteria = otherCriteria;
}
@Override
public List<Person> filter(List<Person> persons) {
List<Person> personList = criteria.filter(persons);
List<Person> list = otherCriteria.filter(persons);
for(Person p : list) {
// 将两个过滤之后进行合并
// 判断是否包含
if(!personList.contains(p)) {
personList.add(p);
}
}
return personList;
}
}
第五步:创建Demo
public class Demo {
public static void main(String[] args) {
Person p1 = new Person("john", "male", "single");
Person p2 = new Person("tom", "male", "married");
Person p3 = new Person("laura", "female", "single");
Person p4 = new Person("bobby", "female", "married");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
// 创建过滤器
CriteriaMale criteriaMale = new CriteriaMale();
CriteriaFemale criteriaFemale = new CriteriaFemale();
CriteriaSingle criteriaSingle = new CriteriaSingle();
AndCriteria andCriteria = new AndCriteria(criteriaFemale, criteriaSingle);
OrCriteria orCriteria = new OrCriteria(criteriaMale, criteriaSingle);
System.out.println("========过滤男士========");
print(criteriaMale.filter(list));
System.out.println("========过滤女士========");
print(criteriaFemale.filter(list));
System.out.println("========过滤单身女士========");
print(andCriteria.filter(list));
System.out.println("========过滤男士或单身========");
print(orCriteria.filter(list));
}
// 打印方法
private static void print(List<Person> list) {
for(Person p : list) {
System.out.println(p);
}
}
}
(9)组合模式
- 组合模式(Composite Pattern),又叫部分整体模式。把一组相似的对象当作一个单一的对象。
- 组合模式依据树型结构来组合对象,用来表示部分以及整体的层次。
- 将对象组合成树型结构以表示”部分-整体“之间的关系,使得用户对单个对象和组合对象的使用具有一致性。
第一步:创建一个员工对象Employee
/**
* 员工类
*/
public class Employee {
private String name;
private String dept;
private float salary;
private List<Employee> subordinates;
public Employee(String name, String dept, float salary) {
this.name = name;
this.dept = dept;
this.salary = salary;
subordinates = new ArrayList<>();
}
// 添加下属
public void add(Employee employee) {
subordinates.add(employee);
}
// 删除下属
public void remove(Employee e) {
subordinates.remove(e);
}
// 获得下属
public List<Employee> getSubordinates() {
return subordinates;
}
@Override
public String toString() {
return "Employee[" + "name:" + name + ", dept:" + dept + ", salary:" + salary + "]";
}
}
public class Demo {
public static void main(String[] args) {
// 创建一个CEO
Employee CEO = new Employee("john", "CEO", 10000);
// 创建两个负责人
Employee headSalary = new Employee("Robert", "head sales", 5000);
Employee headMarketing = new Employee("michel", "head marketing", 5000);
CEO.add(headSalary);
CEO.add(headMarketing);
// 创建四个员工
Employee clerk1 = new Employee("laura", "marketing", 4000);
Employee clerk2 = new Employee("bob", "marketing", 4000);
Employee salesExecutive1 = new Employee("richard", "sales", 4500);
Employee salesExecutive2 = new Employee("rob", "sales", 4500);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
headSalary.add(salesExecutive1);
headSalary.add(salesExecutive2);
//打印CEO
System.out.println(CEO);
// 遍历CEO的下属
for(Employee head : CEO.getSubordinates()) {
System.out.println(head);
// 遍历负责人的下属
for(Employee e : head.getSubordinates()) {
System.out.println(e);
}
}
}
}
(10)装饰者模式
- 装饰者模式(Decorator Pattern)允许向一个现有对象添加一个新的功能,同时又不改变其结构。
- 创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
- 一般我们扩展一个类经常使用继承方式实现,装饰者模式比子类更加灵活。
- 用于扩展一个类的功能,动态的增加和撤销。(可代替继承)
- 实现:我们将创建一个Shape接口以及实现该接口的实体类。然后创建一个实现Shape接口的抽象装饰类
ShapeDecorator
,并把Shape作为它的实例变量。RedShapeDecorator是实现ShapeDecorator的实体类。
第一步:创建Shape接口以及该接口的实现类。
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle: draw circle");
}
}
第二步:创建装饰者ShapeDecorator抽象类,以及它的实现子类RedShapeDecorator。
/**
* 装饰者抽象类
*/
public abstract class ShapeDecorator implements Shape {
protected Shape shape;
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
public abstract void draw();
}
/**
* 装饰者抽象类的实现类
*/
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape shape) {
super(shape);
}
@Override
public void draw() {
shape.draw();
setRed(shape);
}
// 增加方法
public void setRed(Shape shape) {
System.out.println("draw red!!!!");
}
}
第三步:进行测试
public class Demo {
public static void main(String[] args) {
// 新建一个形状
Shape circle = new Circle();
// 创建一个装饰者类
ShapeDecorator decorator = new RedShapeDecorator(circle);
// 未经装饰的方法调用
circle.draw();
// 装饰者之后的方法调用
decorator.draw();
}
}
(11)外观模式
- 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供一个客户端可以访问系统的接口。
- 降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
- 客户端只调用外观类,内部实现细节以及复杂性进行隐藏。
第一步:创建接口以及它的实现类
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("circle: draw");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("rectangle: draw");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("square: draw");
}
}
第二步:创建外观类ShapeMaker
/**
* 外观类,实现对这些复杂类的调用
*/
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
// 分别对方法进行调用
public void drawCircle() {
circle.draw();
}
public void drawRectangle() {
rectangle.draw();
}
public void drawSquare() {
square.draw();
}
}
第三步:创建Demo测试
public class FacadePatternDemo {
public static void main(String[] args) {
// 使用外观类调用方法
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
(12)享元模式
- 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。
- 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
- 核心思想:使用一个Map将对象存起来,使用时先向Map获取对象,获取不到在创建。
第一步:创建接口以及实现类
public interface Shape {
void draw();
}
public class Circle implements Shape {
private int x;
private int y;
private int radius;
private String color;
public Circle(String color) {
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.printf("Circle draw [color: %s, x:%d, y:%d, radius:%d]\n", color, x, y, radius);
}
}
第二步:创建一个享元工厂,使用Map存储对象
/**
* 享元工厂,存在该对象就直接使用,不存在就创建
*/
public class ShapeFactory {
// 使用一个map判断对象是否已经存在
private static final Map<String, Shape> map = new HashMap<>();
public static Shape getCircle(String color) {
// 尝试获取对象
Circle circle = (Circle) map.get(color);
if(circle == null) {
// 等于null,说明该对象不存在
circle = new Circle(color);
map.put(color, circle);
System.out.println("创建了" + color + "颜色的圆!");
}
return circle;
}
}
第三步:创建测试Demo
public class FlyWeightPatternDemo {
public static void main(String[] args) {
// 第一次获取一个红色的圆,获取不到,将会创建
Circle redCircle1 = (Circle) ShapeFactory.getCircle("red");
redCircle1.setX(10);
redCircle1.setY(20);
redCircle1.setRadius(50);
redCircle1.draw();
// 第二次获取到的是第一个创建的圆
Circle redCircle2 = (Circle) ShapeFactory.getCircle("red");
redCircle2.draw();
}
}
(13)代理模式
- 代理模式(Proxy Pattern),一个类代表另一个类的功能。
- 主要解决在直接访问对象时带来的问题。比如要访问的对象在远程的机器上。
- 如:远程代理、虚拟代理、防火墙代理。
第一步:创建一个图片接口以及实现类
public interface Image {
void display();
}
/**
* 真实图片类
*/
public class RealImage implements Image{
private String filename;
public RealImage(String filename) {
loadImage(filename);
this.filename = filename;
}
@Override
public void display() {
System.out.println("read image display " + filename);
}
// 加载图片的方法
public void loadImage(String filename) {
System.out.println("loading " + filename);
}
}
第二步:创建一个代理的类,访问图片时不用重复加载
/**
* 代理图片对象:减少真实图片的加载
*/
public class ProxyImage implements Image {
private String filename;
private RealImage realImage;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if(realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
第三步:创建测试类
public class ProxyDemo {
public static void main(String[] args) {
// 使用代理类获取图片
Image proxyImage = new ProxyImage("heat.jpg");
// 第一次加载需要创建真实图片类
proxyImage.display();
// 第二次不需要
proxyImage.display();
}
}
(14)责任链模式
- 责任链模式(Chain Of Responsibility Pattern)为请求创建了一个接收者对象的链。
- 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,它会把相同的请求传给下一个接收者。
- 避免请求者和接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且连着这条链传递请求,直到有对象处理它为止。
- 我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
第一步:创建抽象记录日志类
/**
* 抽象的记录器类
*/
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
// 责任链中的下一个元素
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
// 根据日志级别输出日志
public void logMessage(int level, String message) {
// 判断是否能够打印当前日志
if(this.level <= level) {
// 打印日志
write(message);
// 输出日志之后,低级别的就不再重复打印
return;
}
// 向责任链下面传递
if(nextLogger != null) {
nextLogger.logMessage(level, message);
}
}
// 打印日志的抽象方法
protected abstract void write(String message);
}
第二步:创建不同级别的日志类
/**
* 控制台打印日志
*/
public class ConsoleLogger extends AbstractLogger{
// 传入日志的级别
public ConsoleLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("console logger: " + message);
}
}
/**
* 文件记录日志
*/
public class FileLogger extends AbstractLogger {
public FileLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("file logger: " + message);
}
}
/**
* 错误记录日志
*/
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level) {
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("error logger: " + message);
}
}
第三步:创建测试Demo
public class ChainPatternDemo {
// 获得责任链方法
public static AbstractLogger getChainOfLogger() {
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
// 设置责任链
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
// 获取责任链打印日志
AbstractLogger chainOfLogger = getChainOfLogger();
// 最低日志
chainOfLogger.logMessage(AbstractLogger.INFO, "控制台输出!");
chainOfLogger.logMessage(AbstractLogger.DEBUG, "debug输出!");
// 最高级别日志
chainOfLogger.logMessage(AbstractLogger.ERROR, "错误输出!");
}
}
(15)命令模式
- 命令模式(Command Pattern),请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
- 将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。
- 命令模式结构示意图:
- 我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。实体命令类 BuyStock 和 SellStock,实现了 Order 接口,将执行实际的命令处理。创建作为调用对象的类 Broker,它接受订单并能下订单。Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。CommandPatternDemo 类使用 Broker 类来演示命令模式。
第一步:创建一个命令接口Order订单接口
public interface Order {
void execute();
}
第二步:创建一个请求类Stock库存类
/**
* 库存类
*/
public class Stock {
private String name = "ABC";
private int quantity = 10;
// 买入库存方法
public void buy() {
System.out.println("stock [ name : " + name + ", quantity : " + quantity + "] bought");
}
// 卖出库存方法
public void sell() {
System.out.println("stock [ name : " + name + ", quantity : " + quantity + "] sold");
}
}
第三步:创建Order接口的实现类BuyStock和SellStock代表不同的行为
/**
* 买入库存
*/
public class BuyStock implements Order {
private Stock abcStock;
public BuyStock(Stock abcStock) {
this.abcStock = abcStock;
}
@Override
public void execute() {
abcStock.buy();
}
}
/**
* 销售库存
*/
public class SellStock implements Order {
private Stock abcStock;
public SellStock(Stock abcStock) {
this.abcStock = abcStock;
}
@Override
public void execute() {
abcStock.sell();
}
}
第四步:创建一个经理类Broker对Order订单进行管理
/**
* 经理类,下订单
*/
public class Broker {
private List<Order> orderList = new ArrayList<>();
// 加入一个订单
public void takeOrder(Order order) {
orderList.add(order);
}
// 执行订单方法
public void palceOrder() {
// 遍历订单
for(Order o : orderList) {
// 执行订单
o.execute();
}
// 执行完之后清除所有订单
orderList.clear();
}
}
第五步:创建测试Demo
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建一个经理人
Broker broker = new Broker();
// 创建一个仓库
Stock stock = new Stock();
// 加入不同的订单
broker.takeOrder(new BuyStock(stock));
broker.takeOrder(new SellStock(stock));
// 执行所有的订单
broker.palceOrder();
}
}
(16)解释器模式
- 解释器模式(Interpreter Pattern),实现一个表达式接口,该接口解释一个特定的上下文。该接口解释一个特定的上下文,这种模式被用在SQL解析、符号引擎处理等。
- 对于一些固定文法构建一个解释句子的解释器。
- 我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpression、AndExpression 用于创建组合式表达式。InterpreterPatternDemo,我们的演示类使用 Expression 类创建规则和演示表达式的解析。
第一步:定义一个解释器接口
/**
* 解释器接口
*/
public interface Expression {
boolean interpret(String context);
}
第二步:定义一个解释器接口的实现类
/**
* 终端解释器:实现解释器接口
*/
public class TerminalExpression implements Expression{
private String data;
// 传入需要解释的数据
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
// 返回解释结果
return context.contains(data);
}
}
第三步:定义组合解释器
/**
* 两个解释器,一个返回true就为true
*/
public class OrExpression implements Expression{
// 定义两个解释器
private Expression e1;
private Expression e2;
public OrExpression(Expression e1, Expression e2) {
this.e1 = e1;
this.e2 = e2;
}
@Override
public boolean interpret(String context) {
return e1.interpret(context) || e2.interpret(context);
}
}
/**
* 两个解释器都为true,才为true
*/
public class AndExpression implements Expression {
private Expression e1;
private Expression e2;
public AndExpression(Expression e1, Expression e2) {
this.e1 = e1;
this.e2 = e2;
}
@Override
public boolean interpret(String context) {
return e1.interpret(context) && e2.interpret(context);
}
}
第四步:定义测试Demo
public class InterpreterPatternDemo {
// 定义一个解释规则:tom和jack都是男
public static Expression getMaleExpression() {
TerminalExpression tom = new TerminalExpression("tom");
TerminalExpression jack = new TerminalExpression("jack");
return new OrExpression(tom, jack);
}
// 定义一个规则:julie已婚
public static Expression getMarriedExpression() {
TerminalExpression julie = new TerminalExpression("julie");
TerminalExpression married = new TerminalExpression("married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression maleExpression = getMaleExpression();
System.out.println("tom is male? " + maleExpression.interpret("tom"));
Expression marriedExpression = getMarriedExpression();
System.out.println("julie is married? " + marriedExpression.interpret("julie married"));
}
}
(17)迭代器模式
- 迭代器模式(Iterator Pattern)用于顺序访问集合对象的元素,不需要知道集合对象的底层实现。
- 我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。IteratorPatternDemo,我们的演示类使用实体类 CommonContains 来打印 CommonContains 中存储的元素。
第一步:创建迭代器接口和容器接口
/**
* 迭代器接口
*/
public interface Iterator<T> {
boolean hasNext();
T next();
}
/**
* 容器接口
*/
public interface Container<T> {
// 获取迭代对象
Iterator<T> getIterator();
}
第二步:创建容器接口的实现类
/**
* 容器的实现类:一个普通的容器
*/
public class CommonContainer<T> implements Container<T> {
private T[] values;
public CommonContainer(T[] values) {
this.values = values;
}
@Override
public Iterator<T> getIterator() {
return new CommonIterator<>();
}
private class CommonIterator<T> implements Iterator<T> {
// 定义一个指针
int index;
// 判断是否有下一个
@Override
public boolean hasNext() {
return index < values.length;
}
// 获取下一个元素
@Override
public T next() {
if(this.hasNext()) {
return (T) values[index++];
}
return null;
}
}
}
第三步:创建Demo测试
public class IteratorPatternDemo {
public static void main(String[] args) {
String[] names = {"tom", "jack", "jerry"};
// 通用容器
Container<String> container = new CommonContainer<>(names);
// 获得容器的迭代器
Iterator<String> iterator = container.getIterator();
// 获取每个元素
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
(18)中介者模式
- 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。提供一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
- 我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。
- MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。
第一步:创建一个User用户类
/**
* 用户类
*/
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void showMessage(String message) {
ChatRoom.showMessage(this, message);
}
}
第二步:创建一个聊天中介类ChatRoom
/**
* 创建一个聊天室作为中介类
*/
public class ChatRoom {
public static void showMessage(User user, String message) {
System.out.println("User[" + user.getName() + "] : " + message);
}
}
第三步:创建Demo
public class MediatorPatternDemo {
public static void main(String[] args) {
User tom = new User("tom");
User jerry = new User("jerry");
tom.showMessage("hello jerry");
jerry.showMessage("hi tom");
}
}
(19)备忘录模式
- 备忘录模式(Memento Patern)保存一个对象的某种状态,必便在适当的时候恢复对象。
- 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
- 应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理。
第一步:创建要被恢复的对象的状态
/**
* 包含要被恢复对象的状态
*/
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
第二步:创建并在Memento中存储状态
/**
* 在Memento对象中存储状态
*/
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
// 保存状态到Memento中
public Memento saveStateToMemento() {
return new Memento(state);
}
// 获取Memento中的状态
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
第三步:从Memento中恢复对象的状态
/**
* 从Memento中恢复对象的状态
*/
public class CareTaker {
private List<Memento> mementoList = new ArrayList<>();
// 添加需要恢复的状态
public void add(Memento memento) {
mementoList.add(memento);
}
// 获取状态,进行恢复
public Memento get(int index) {
return mementoList.get(index);
}
}
第四步:创建Demo
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("state #1");
// 保存当前状态
Memento memento1 = originator.saveStateToMemento();
// 添加状态,以便恢复
careTaker.add(memento1);
originator.setState("state #2");
Memento memento2 = originator.saveStateToMemento();
careTaker.add(memento2);
// 获取状态
System.out.println("当前状态:" + originator.getState());
System.out.println("第一次保存的状态:" + careTaker.get(0).getState());
System.out.println("第二次保存的状态:" + careTaker.get(1).getState());
// 恢复状态
originator.getStateFromMemento(careTaker.get(0));
System.out.println("恢复后的当前状态:" + originator.getState());
}
}
(20)观察者模式
- 当对象间存在一对多的关系时,使用观察者模式(Observer Pattern)。当一个对象被修改时,会自动通知依赖它的对象。
- 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。
- ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。
第一步:创建一个被观察的对象Subject类
/**
* 创建实体类
*/
public class Subject {
// 将所有的观察者都存放在List中,发生改变就通知它们
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// 改变值的时候,就通知所有的观察者
notifyAllObservers();
}
// 将观察者添加到list中
public void attach(Observer observer) {
observers.add(observer);
}
// 通知所有的观察者
public void notifyAllObservers() {
for (Observer o : observers) {
// 调用观察者的update进行修改
o.update();
}
}
}
第二步:创建观察者抽象类
/**
* 观察者抽象类
*/
public abstract class Observer {
// 需要观察的对象
protected Subject subject;
public abstract void update();
}
第三步:创建观察者的实现类
/**
* 观察者实现类
*/
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
// 转化为二进制
System.out.println("Binary String: " + Integer.toBinaryString(subject.getState()));
}
}
/**
* 观察者实现类
*/
public class HexaObserver extends Observer {
public HexaObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
// 转化为16进制
System.out.println("Hex String: " + Integer.toHexString(subject.getState()));
}
}
第四步:创建Demo
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
// 新建两个观察者
new BinaryObserver(subject);
new HexaObserver(subject);
// 改变subject的state,会通知观察者
subject.setState(15);
subject.setState(18);
}
}
(21)状态模式
- 在状态模式中(State Pattern),类的行为是基于它的状态改变的。
- 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
- 我们将创建一个 State 接口和实现了 State 接口的实体状态类。Context 是一个带有某个状态的类。
- StatePatternDemo,我们的演示类使用 Context 和状态对象来演示 Context 在状态改变时的行为变化。
第一步:创建一个状态接口State
/**
* 状态接口
*/
public interface State {
void doAction(Context context);
}
第二步:创建一个上下文类Context
public class Context {
private State state;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
第三步:创建State的实现类
public class StartState implements State{
@Override
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
@Override
public String toString() {
return "Start State";
}
}
public class StopState implements State {
@Override
public void doAction(Context context) {
System.out.println("Player is in stop state");
context.setState(this);
}
@Override
public String toString() {
return "stop state";
}
}
第四步:创建Demo
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
// 新建一个状态
StartState startState = new StartState();
// 设置状态
startState.doAction(context);
// 获取当前状态
State state = context.getState();
System.out.println(state);
}
}
(22)空对象模式
- 在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。
- 我们将创建一个定义操作(在这里,是客户的名称)的 AbstractCustomer 抽象类,和扩展了 AbstractCustomer 类的实体类。工厂类 CustomerFactory 基于客户传递的名字来返回 RealCustomer 或 NullCustomer 对象。
- NullPatternDemo,我们的演示类使用 CustomerFactory 来演示空对象模式的用法。
第一步:创建一个抽象类
/**
* 抽象的消费者类
*/
public abstract class AbstractCustomer {
protected String name;
// 是否为空
public abstract boolean isNil();
public abstract String getName();
}
第二步:创建实现类
/**
* 真实的消费者类
*/
public class RealCustomer extends AbstractCustomer {
public RealCustomer(String name) {
this.name = name;
}
@Override
public boolean isNil() {
return false;
}
@Override
public String getName() {
return name;
}
}
/**
* 空对象的消费者类
*/
public class NullCustomer extends AbstractCustomer{
@Override
public boolean isNil() {
return true;
}
@Override
public String getName() {
return "Not Available in Customer Database";
}
}
第三步:创建工厂类
/**
* 创建消费者的工厂类
*/
public class CustomerFactory {
private static final String[] NAMES = {"tom","jerry","jack"};
public static AbstractCustomer getCustomer(String name) {
for(String s : NAMES) {
// 如果能够找到名字就创建一个真实的对象
if(name.equals(s)) {
return new RealCustomer(name);
}
}
return new NullCustomer();
}
}
第四步:创建Demo
public class NullObjectPatternDemo {
public static void main(String[] args) {
// 能够获取到真实对象
AbstractCustomer tom = CustomerFactory.getCustomer("tom");
// 获取到自定义的一个空对象
AbstractCustomer hanMeimei = CustomerFactory.getCustomer("HanMeimei");
System.out.println(tom.getName());
System.out.println(hanMeimei.getName());
}
}
(23)策略模式
- 在策略模式(Strategy)中,一个类的行为或其算法可以在运行时更改。
- 我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
- StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。
第一步:创建一个策略接口
/**
* 策略接口
*/
public interface Strategy {
// 做运算的策略
int doOperation(int num1, int num2);
}
第二步:创建接口的实现类
/**
* 做加法的运算策略
*/
public class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
/**
* 减法策略
*/
public class OperationSubsract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
/**
* 乘法策略
*/
public class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
第三步:创建一个Context类,统一调用运算
/**
* 统一调用
*/
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
// 统一执行方法
public int executStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
第四步:创建Demo
public class StrategyPatternDemo {
public static void main(String[] args) {
// 调用运算策略
Context context = new Context(new OperationAdd());
System.out.println("4 + 5 = " + context.executStrategy(4,5));
context = new Context(new OperationMultiply());
System.out.println("4 * 5 = " + context.executStrategy(4,5));
}
}
(24)模板模式
- 模板模式(Template Pattern),一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现。但调用将以抽象类中定义的方法执行。
- 我们将创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
- VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。
第一步:创建一个Game抽象类
/**
* 游戏抽象类
*/
public abstract class Game {
abstract void init();
abstract void start();
abstract void end();
public final void play() {
// 初始化游戏
init();
// 开始游戏
start();
// 结束游戏
end();
}
}
第二步:创建抽象类的实现
public class BasketBall extends Game {
@Override
void init() {
System.out.println("BasketBall init");
}
@Override
void start() {
System.out.println("BasketBall start");
}
@Override
void end() {
System.out.println("BasketBall end");
}
}
public class FootBall extends Game {
@Override
void init() {
System.out.println("FootBall init");
}
@Override
void start() {
System.out.println("FootBall start");
}
@Override
void end() {
System.out.println("FootBall end");
}
}
第三步:创建Demo
public class TemplatePatternDemo {
public static void main(String[] args) {
Game basketball = new BasketBall();
basketball.play();
System.out.println();
Game football = new FootBall();
football.play();
}
}
(25)访问者模式
- 访问者模式(Visitor Pattern),==使用一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。==根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
- 我们将创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
- VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。
第一步:创建一个元素接口
/**
* 元素接口
*/
public interface ComputerPart {
// 需要一个访问者
void accept(ComputerPartVisitor computerPartVisitor);
}
第二步:创建访问者接口
/**
* 访问者接口
*/
public interface ComputerPartVisitor {
void visit(Computer computer);
void visit(Mouse mouse);
void visit(Keyboard keyboard);
void visit(Monitor monitor);
}
第三步:创建元素接口的实现类
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
/**
* 电脑实体类
*/
public class Computer implements ComputerPart {
ComputerPart[] parts;
public Computer() {
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for(ComputerPart part : parts) {
part.accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
第四步:创建访问者的实现类
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("computer display");
}
@Override
public void visit(Mouse mouse) {
System.out.println("computer mouse");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("computer keyboard");
}
@Override
public void visit(Monitor monitor) {
System.out.println("computer monitor");
}
}
第五步:创建Demo
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}