设计模式
文章目录
- 设计模式
- 七大原则:
- UML类图:
- UML 本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等,如右图: ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/04be1f21e5ddb8e93af81fec13d3ba2f.png)
- 设计模式:
七大原则:
1.单一职责原则
对类来说的,即一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更而改变 A 时,可能造成职责 2 执行错误,所以需要将类 A 的粒度分解为 A1,A2
2.接口隔离原则
1) 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
2)图解
3. 依赖倒转原则
依赖倒转原则(Dependence Inversion Principle)是指:
1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象
2) 抽象不应该依赖细节,细节应该依赖抽象
3) 依赖倒转(倒置)的中心思想是面向接口编程
4) 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
5) 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
依赖关系传递的三种方式:
1) 接口传递
2) 构造方法传递应用案例代码
3) setter 方式传递
4.里氏替换原则
1) 里氏替换原则(Liskov Substitution Principle)在 1988 年,由麻省理工学院的以为姓里的女士提出的。
2) 如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型T2 是类型 T1 的子类型。换句 话说,所有引用基类的地方必须能透明地使用其子类的对象。
3) 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
4) 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。.
UML类图:
1.各种符号规定
UML 本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等,如右图:
2.UML图
1) 用例图(use case)
2) 静态结构图:类图、对象图、包图、组件图、部署图
3) 动态行为图:交互图(时序图与协作图)、状态图、活动图
介绍:
1) 用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系。
2) 类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合。
关系:
1)依赖关系:
小结
1. 类中用到了对方
2. 如果是类的成员属性
3. 如果是方法的返回类型
4. 是方法接收的参数类型
5. 方法中使用到
2)泛化关系:
1. 泛化关系实际上就是继承关系
2. 如果 A 类继承了 B 类,我们就说 A 和 B 存在泛化关系
3) 实现关系:
4) 关联关系:
5) 聚合关系:
1.基本介绍
聚合关系(Aggregation)表示的是整体和部分的关系,整体与部分可以分开。聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。
如:一台电脑由键盘(keyboard)、显示器(monitor),鼠标等组成;组成电脑的各个配件是可以从电脑上分离出来的,使用带空心菱形的实线来表示:
6)组合关系:
介绍:
组合关系:也是整体与部分的关系,但是整体与部分不可以分开。
再看一个案例:在程序中我们定义实体:Person 与 IDCard、Head, 那么 Head 和 Person 就是 组合,IDCard 和Person 就是聚合。
但是如果在程序中 Person 实体中定义了对 IDCard 进行级联删除,即删除 Person 时连同 IDCard 一起删除,那么 IDCard 和 Person 就是组合了
设计模式:
1.介绍:
1) 设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
2) 设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度。
3) <<设计模式>> 是经典的书,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称 “四人组 GOF”)
4) 设计模式并不局限于某种语言,java,php,c++ 都有设计模式.
2.类型:
设计模式分为三种类型,共 23 种
1) 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
2) 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3) 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
注意:不同的书籍上对分类和名称略有差别
3.单例模式
1) 饿汉式(静态常量)
2) 饿汉式(静态代码块)
3) 懒汉式(线程不安全)
4) 懒汉式(线程安全,同步方法)
5) 懒汉式(线程安全,同步代码块)
6) 双重检查
7) 静态内部类
8) 枚举
饿汉式(静态常量):
/**
* 饿汉式(静态常量)
*/
class SingtonStaticFinal {
// 将构造器私有化
private SingtonStaticFinal() {}
private final static SingtonStaticFinal obj = new SingtonStaticFinal();
public static SingtonStaticFinal getObj(){
return obj;
}
}
优缺点说明:
-
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
-
缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
-
这种方式基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,在单例模式中大多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
-
结论:这种单例模式可用,可能造成内存浪费
饿汉式(静态代码块):
/**
* 饿汉式(静态代码块)
*/
class SingtonStaticCode {
// 将构造器私有化
private SingtonStaticCode() {}
private final static SingtonStaticCode obj;
static {
obj = new SingtonStaticCode();
}
public static SingtonStaticCode getObj(){
return obj;
}
}
优缺点说明:
-
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
-
结论:这种单例模式可用,但是可能造成内存浪费
赖汉式(线程不安全):
/**
* 赖汉式(线程不安全)
*/
class SingtonNoThread {
private SingtonNoThread() {}
private static SingtonNoThread obj;
public static SingtonNoThread getObj() {
if(obj == null) {
obj = new SingtonNoThread();
}
return obj;
}
}
优缺点说明:
-
起到了 Lazy Loading 的效果,但是只能在单线程下使用。
-
如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
-
结论:在实际开发中,不要使用这种方式.
赖汉式(线程安全,同步方法):
/**
* 赖汉式(线程安全, 同步方法)
*/
class SingtonThread {
private SingtonThread() {}
private static SingtonThread obj;
public static synchronized SingtonThread getObj() {
if(obj == null) {
obj = new SingtonThread();
}
return obj;
}
}
优缺点说明:
-
解决了线程安全问题
-
效率太低了,每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。方法进行同步效率太低
-
结论:在实际开发中,不推荐使用这种方式
赖汉式(线程安全, 同步代码块):
/**
* 赖汉式(线程不安全, 同步代码块)
*/
class SingtonNoThreadCode {
private SingtonNoThreadCode() {}
private static SingtonNoThreadCode obj;
public static SingtonNoThreadCode getObj() {
if(obj == null) {
synchronized (SingtonNoThreadCode.class){
obj = new SingtonNoThreadCode();
}
}
return obj;
}
}
不推荐使用
双重检查:
/**
* 双重检查
*/
class SingtonDuplicationCheck {
private SingtonDuplicationCheck() {}
private static SingtonDuplicationCheck obj;
public static SingtonDuplicationCheck getObj() {
if(obj == null) {
synchronized (SingtonDuplicationCheck.class){
if (obj == null) {
obj = new SingtonDuplicationCheck();
}
}
}
return obj;
}
}
优缺点说明:
-
Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次 if (singleton == null)检查,这样就可以保证线程安全了。
-
这样,实例化代码只用执行一次,后面再次访问时,判断 if (singleton == null),直接 return 实例化对象,也避免的反复进行方法同步.
-
线程安全;延迟加载;效率较高
-
结论:在实际开发中,推荐使用这种单例设计模式
静态内部类:
/**
* 静态内部类
*/
class SingtonStaticInnerClass {
private SingtonStaticInnerClass () {}
private static class SingtonObj {
private static final SingtonStaticInnerClass obj = new SingtonStaticInnerClass();
}
public static SingtonStaticInnerClass getObj () {
return SingtonObj.obj;
}
}
优缺点说明:
-
这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
-
静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
-
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
-
优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
-
结论:推荐使用.
枚举:
/**
* 枚举实现
*/
class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
优缺点说明:
-
这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
-
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
结论:推荐使用
4.工厂模式
简单工厂模式:
1) 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
2) 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式
UML类图:
代码:
package com.atguigu.factory.simplefactory.pizzastore.order;
import com.atguigu.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.atguigu.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.atguigu.factory.simplefactory.pizzastore.pizza.PepperPizza;
import com.atguigu.factory.simplefactory.pizzastore.pizza.Pizza;
//简单工厂类
public class SimpleFactory {
//更加 orderType 返回对应的 Pizza 对象
public Pizza createPizza(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
//简单工厂模式 也叫 静态工厂模式
public static Pizza createPizza2(String orderType) {
Pizza pizza = null;
System.out.println("使用简单工厂模式 2");
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName(" 奶酪披萨 ");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
//OrderPizza.java
package com.atguigu.factory.simplefactory.pizzastore.order;
import java.io.BufferedReader;
import java.io.IOException;import java.io.InputStreamReader;
import com.atguigu.factory.simplefactory.pizzastore.pizza.Pizza;
public class OrderPizza {
// 构造器
// public OrderPizza() {
// Pizza pizza = null;
// String orderType; // 订购披萨的类型
// do {
// orderType = getType();
// if (orderType.equals("greek")) {
// pizza = new GreekPizza();
// pizza.setName(" 希腊披萨 ");
// } else if (orderType.equals("cheese")) {
// pizza = new CheesePizza();
// pizza.setName(" 奶酪披萨 ");
// } else if (orderType.equals("pepper")) {
// pizza = new PepperPizza();
// pizza.setName("胡椒披萨");
// } else {
// break;
// }
// //输出 pizza 制作过程
// pizza.prepare();
// pizza.bake();
// pizza.cut();
// pizza.box();
//
// } while (true);
// }
//定义一个简单工厂对象SimpleFactory simpleFactory; Pizza pizza = null;
//构造器
public OrderPizza(SimpleFactory simpleFactory) {
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory) {
String orderType = ""; //用户输入的
this.simpleFactory = simpleFactory; //设置简单工厂对象
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
//输出 pizza
if (pizza != null) { //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println(" 订购披萨失败 ");
break;
}
} while (true);
}
// 写一个方法,可以获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
工厂方法模式:
1) 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
2) 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
UML类图:
代码:
//OrderPizza.java 类
package com.atguigu.factory.factorymethod.pizzastore.order;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import com.atguigu.factory.factorymethod.pizzastore.pizza.Pizza;
public abstract class OrderPizza {
//定义一个抽象方法,createPizza , 让各个工厂子类自己实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购披萨的类型
do {
orderType = getType();
pizza = createPizza(orderType); //抽象方法,由工厂子类完成
//输出 pizza 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
// 写一个方法,可以获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
package com.atguigu.factory.factorymethod.pizzastore.order;
import com.atguigu.factory.factorymethod.pizzastore.pizza.BJCheesePizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.BJPepperPizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.Pizza;
public class BJOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
// TODO Auto-generated method stub return pizza;
}
}
package com.atguigu.factory.factorymethod.pizzastore.order;
import com.atguigu.factory.factorymethod.pizzastore.pizza.BJCheesePizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.BJPepperPizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.LDCheesePizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.LDPepperPizza;import com.atguigu.factory.factorymethod.pizzastore.pizza.Pizza;
public class LDOrderPizza extends OrderPizza {
@Override
Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
// TODO Auto-generated method stub return pizza;
}
}
抽象工厂模式:
-
抽象工厂模式:定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
-
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
-
从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
-
将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
UML类图:
代码:
package com.atguigu.factory.absfactory.pizzastore.order;
import com.atguigu.factory.absfactory.pizzastore.pizza.Pizza;
//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
//让下面的工厂子类来 具体实现
public Pizza createPizza(String orderType);
}
package com.atguigu.factory.absfactory.pizzastore.order;
import com.atguigu.factory.absfactory.pizzastore.pizza.BJCheesePizza;
import com.atguigu.factory.absfactory.pizzastore.pizza.BJPepperPizza;
import com.atguigu.factory.absfactory.pizzastore.pizza.Pizza;
//这是工厂子类
public class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工厂模式~");
// TODO Auto-generated method stub
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
package com.atguigu.factory.absfactory.pizzastore.order;
import com.atguigu.factory.absfactory.pizzastore.pizza.LDCheesePizza;import com.atguigu.factory.absfactory.pizzastore.pizza.LDPepperPizza;import com.atguigu.factory.absfactory.pizzastore.pizza.Pizza;
public class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("~使用的是抽象工厂模式~");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
//OrderPizza.java
package com.atguigu.factory.absfactory.pizzastore.order;
import java.io.BufferedReader;
import java.io.IOException;import java.io.InputStreamReader;
import com.atguigu.factory.absfactory.pizzastore.pizza.Pizza;
public class OrderPizza {
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory) {
setFactory(factory);
}
private void setFactory(AbsFactory factory) {
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类,也可能是伦敦的工厂子类
pizza = factory.createPizza(orderType);
if (pizza != null) { // 订 购 ok
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败");
break;
}
} while (true);
}
// 写一个方法,可以获取客户希望订购的披萨种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String st = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
小结:
- 工厂模式的意义
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
-
三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
-
设计模式的依赖抽象原则
Ø 创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。
Ø 不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)
Ø 不要覆盖基类中已经实现的方法。
5.原型模式
介绍:
-
原型模式(Prototype 模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
-
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
-
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象**.clone**()
-
形象的理解:孙大圣拔出猴毛, 变出其它孙大圣
UML类图:
浅拷贝代码:
package com.atguigu.prototype.improve;
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private String address = "蒙古羊";
public Sheep friend; //是对象, 克隆是会如何处理, 默认是浅拷贝
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
}
//克隆该实例,使用默认的 clone 方法来完成
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
// TODO: handle exception System.out.println(e.getMessage());
}
// TODO Auto-generated method stub return sheep;
}
}
package com.atguigu.prototype.improve;
public class Client {
public static void main(String[] args) {
System.out.println("原型模式完成对象的创建");
// TODO Auto-generated method stub
Sheep sheep = new Sheep("tom", 1, "白色");
sheep.friend = new Sheep("jack", 2, "黑色");
Sheep sheep2 = (Sheep) sheep.clone();
//克隆Sheep
sheep3 = (Sheep)sheep.clone();
//克隆Sheep
sheep4 = (Sheep)sheep.clone();
//克隆Sheep
sheep5 = (Sheep)sheep.clone(); //克隆
System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
}
}
深拷贝代码:
package com.atguigu.prototype.deepclone;
import java.io.Serializable;
public class DeepCloneableTarget implements Serializable, Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是 String , 因此我们这里使用默认的 clone 完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//...
package com.atguigu.prototype.deepclone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DeepProtoType implements Serializable, Cloneable {
public String name; //String 属 性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用 clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
// TODO Auto-generated method stub return deepProtoType;
}
//深拷贝 - 方式 2 通过对象的序列化实现 (推荐)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try
{
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType) ois.readObject();
return copyObj;
} catch(
Exception e)
{
// TODO: handle exception e.printStackTrace();
return null;
} finally
{
//关闭流try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch(
Exception e2)
{
// TODO: handle exception System.out.println(e2.getMessage());
}
}
}
}
//Client.java
package com.atguigu.prototype.deepclone;
public class Client {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
DeepProtoType p = new DeepProtoType();
p.name = "宋江";
p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");
//方式 1 完成深拷贝
// DeepProtoType p2 = (DeepProtoType) p.clone();
//
// System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
// System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
//方式 2 完成深拷贝
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
}
}
6.适配器模式
介绍:
-
适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
-
适配器模式属于结构型模式
-
主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
原理:
-
适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
-
从用户的角度看不到被适配者,是解耦的
-
用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
用户收到反馈结果,感觉只是和目标接口交互
类适配器模式:
应用实例说明
以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们的目 dst(即 目标)是 5V 直流电
UML类图:
代码实现:
package com.atguigu.adapter.classadapter;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(" === 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
package com.atguigu.adapter.classadapter;
//适配接口
public interface IVoltage5V {
public int output5V();
}
package com.atguigu.adapter.classadapter;
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压为 5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于 5V, 不能充电~~");
}
}
}
package com.atguigu.adapter.classadapter;
//被适配的类
public class Voltage220V {
//输出 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
package com.atguigu.adapter.classadapter;
//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
// TODO Auto-generated method stub
//获取到 220V 电压
int srcV = output220V();
int dstV = srcV / 44; //转成 5v
return dstV;
}
}
对象适配器:
基本思路和类的适配器模式相同,只是将 Adapter 类作修改,不是继承 src 类,而是持有 src 类的实例,以解决兼容性的问题。 即:持有 src 类,实现 dst 类接口,完成 src->dst 的适配
应用实例说
以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们的目 dst(即目标)是 5V 直流电,使用对象适配器模式完成。
UML类图:
代码实现:
package com.atguigu.adapter.objectadapter;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(" === 对象适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
package com.atguigu.adapter.objectadapter;
//适配接口
public interface IVoltage5V {
public int output5V();
}
package com.atguigu.adapter.objectadapter;
public class Phone {
//充电
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压为 5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于 5V, 不能充电~~");
}
}
}
package com.atguigu.adapter.objectadapter;
//被适配的类
public class Voltage220V {
//输出 220V 的电压,不变
public int output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
package com.atguigu.adapter.objectadapter;
//适配器类
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V; // 关联关系-聚合
//通过构造器,传入一个 Voltage220V 实例
public VoltageAdapter(Voltage220V voltage220v) {
this.voltage220V = voltage220v;
}
@Override
public int output5V() {
int dst = 0;
if (null != voltage220V) {
int src = voltage220V.output220V();//获取 220V 电压System.out.println("使用对象适配器,进行适配~~");
dst = src / 44;
System.out.println("适配完成,输出的电压为=" + dst);
}
return dst;
}
}
接口适配器:
-
一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
-
核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
-
适用于一个接口不想使用其所有的方法的情况。
UML类图:
代码实现:
package com.atguigu.adapter.interfaceadapter;
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
package com.atguigu.adapter.interfaceadapter;
//在 AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {
//默认实现
public void m1() {
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
}
package com.atguigu.adapter.interfaceadapter;
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
//只需要去覆盖我们 需要使用 接口方法
@Override
public void m1() {
// TODO Auto-generated method stub
System.out.println("使用了 m1 的方法");
}
};
absAdapter.m1();
}
}
适配器模式在SpringMVC 框架应用的源码剖析:
-
SpringMvc 中的 **HandlerAdapter,** 就使用了适配器模式
-
SpringMVC 处理请求的流程回顾
-
使用 HandlerAdapter 的原因分析:
可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断是使用 if else 来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了 OCP 原则。
手动SpringMvc:
UML类图:
-
三种命名方式,是根据 src 是以怎样的形式给到 Adapter(在 Adapter 里的形式)来命名的。
-
类适配器:以类给到,在 Adapter 里,就是将 src 当做类,继承
对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,持有接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,实现
-
Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作。
-
实际开发中,实现起来不拘泥于我们讲解的三种经典形式
7.桥接模式
介绍:
-
桥接模式(Bridge 模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
-
是一种结构型设计模式
-
Bridge 模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展
原理图:
-
Client 类:桥接模式的调用者
-
抽象类(Abstraction) :维护了 Implementor / 即它的实现类 ConcreteImplementorA…, 二者是聚合关系, Abstraction充当桥接类
-
RefinedAbstraction : 是 Abstraction 抽象类的子类
-
Implementor : 行为实现类的接口
-
ConcreteImplementorA /B :行为的具体实现类
-
从 UML 图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系
案例:
UML类图:
代码实现:
package com.atguigu.bridge;
//接口
public interface Brand {
void open();
void close();
void call();
}
package com.atguigu.bridge;
public class Client {
public static void main(String[] args) {
//获取折叠式手机 (样式 + 品牌 )
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("=======================");
Phone phone2 = new FoldedPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
System.out.println("==============");
UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("==============");
UpRightPhone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
package com.atguigu.bridge;
//折叠式手机类,继承 抽象类 Phone
public class FoldedPhone extends Phone {
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 折叠样式手机 ");
}
public void close() {
super.close();
System.out.println(" 折叠样式手机 ");
}
public void call() {
super.call();
System.out.println(" 折叠样式手机 ");
}
}
package com.atguigu.bridge;
public abstract class Phone {
//组合品牌
private Brand brand;
//构造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
brand.close();
}
protected void call() {
brand.call();
}
}
package com.atguigu.bridge;
public class UpRightPhone extends Phone {
//构造器
public UpRightPhone(Brand brand) {
super(brand);
}
public void open() {
super.open();
System.out.println(" 直立样式手机 ");
}
public void close() {
super.close();
System.out.println(" 直立样式手机 ");
}
public void call() {
super.call();
System.out.println(" 直立样式手机 ");
}
}
package com.atguigu.bridge;
public class Vivo implements Brand {
@Override
public void open() {
// TODO Auto-generated method stub System.out.println(" Vivo 手机开机 ");
}
@Override
public void close() {
// TODO Auto-generated method stub System.out.println(" Vivo 手机关机 ");
}
@Override
public void call() {
// TODO Auto-generated method stub System.out.println(" Vivo 手机打电话 ");
}
}
package com.atguigu.bridge;
public class XiaoMi implements Brand {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println(" 小米手机开机 ");
}
@Override
public void close() {
// TODO Auto-generated method stub System.out.println(" 小米手机关机 ");
}
@Override
public void call() {
// TODO Auto-generated method stub System.out.println(" 小米手机打电话 ");
}
}
JDBC源码分析实例UML类图:
桥接模式的注意事项:
-
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
-
对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
-
桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
-
桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
-
桥接模式要求正确识别出系统中两个独立变化的维度**(抽象、和实现)**,因此其使用范围有一定的局限性,即需要有这样的应用场景。
桥接模式其它应用场景
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用.
8.修饰者模式
UML类图:
代码实现:
package com.atguigu.decorator;
//具体的 Decorator, 这里就是调味品
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品 的价格
}
}
package com.atguigu.decorator;
public class Coffee extends Drink {
@Override
public float cost() {
// TODO Auto-generated method stub return super.getPrice();
}
}
package com.atguigu.decorator;
public class CoffeeBar {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
// 1. 点一份 LongBlack
Drink order = new LongBlack();
System.out.println("费用 1=" + order.cost());
System.out.println("描述=" + order.getDes());
// 2. order 加入一份牛奶
order = new Milk(order);
System.out.println("order 加 入 一 份 牛 奶 费 用 =" + order.cost());
System.out.println("order 加入一份牛奶 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入一份巧克力 费 用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());
// 3. order 加入一份巧克力
order = new Chocolate(order);
System.out.println("order 加入一份牛奶 加入 2 份巧克力 费 用 =" + order.cost());
System.out.println("order 加入一份牛奶 加入 2 份巧克力 描述 = " + order.getDes());
System.out.println("===========================");
Drink order2 = new DeCaf();
System.out.println("order2 无因咖啡 费 用 =" + order2.cost());
System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
order2 = new Milk(order2);
System.out.println("order2 无因咖啡 加入一份牛奶 费 用 =" + order2.cost());
System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());
}
}
package com.atguigu.decorator;
public class DeCaf extends Coffee {
public DeCaf() {
setDes(" 无因咖啡 ");
setPrice(1.0f);
}
}
package com.atguigu.decorator;
public class Decorator extends Drink {
private Drink obj;
public Decorator(Drink obj) { //组合
// TODO Auto-generated constructor stub this.obj = obj;
}
@Override
public float cost() {
// TODO Auto-generated method stub
// getPrice 自己价格
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
// obj.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
package com.atguigu.decorator;
public abstract class Drink {
public String des; // 描 述
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法
//子类来实现
public abstract float cost();
}
package com.atguigu.decorator;
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
package com.atguigu.decorator;
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
package com.atguigu.decorator;
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub setDes(" 牛 奶 ");
setPrice(2.0f);
}
}
package com.atguigu.decorator;
public class ShortBlack extends Coffee {
public ShortBlack() {
setDes(" shortblack ");
setPrice(4.0f);
}
}
package com.atguigu.decorator;
public class Soy extends Decorator {
public Soy(Drink obj) {
super(obj);
// TODO Auto-generated constructor stub setDes(" 豆浆 ");
setPrice(1.5f);
}
}
9.组合模式
基本介绍
-
组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体**-**部分”的层次关系。
-
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
-
这种类型的设计模式属于结构型模式。
-
组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
组合模式原理类图
对原理结构图的说明-即(组合模式的角色及职责)
-
**Component** :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component 子部件, Component 可以是抽象类或者接口
-
**Leaf :** 在组合中表示叶子节点,叶子节点没有子节点
-
**Composite** :非叶子节点, 用于存储子部件, 在 Component 接口中实现 子部件的相关操作,比如增加(add), 删除。
应用实例:
- 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。
UML类图:
代码实现:
package com.atguigu.composite;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//从大到小创建对象 学校
OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
//创建 学院
OrganizationComponent computerCollege = new College(" 计 算 机 学 院 ", " 计 算 机 学 院 "); OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
//创建各个学院下面的系(专业)
computerCollege.add(new Department("软件工程", " 软件工程不错 ")); computerCollege.add(new Department("网络工程", " 网络工程不错 "));
computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
//
infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 ")); infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
//将学院加入到 学校university.add(computerCollege); university.add(infoEngineercollege);
//university.print(); infoEngineercollege.print();
}
}
package com.atguigu.composite;
import java.util.ArrayList; import java.util.List;
public class College extends OrganizationComponent {
//List 中 存放的 Department
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public College(String name, String des) { super(name, des);
// TODO Auto-generated constructor stub
}
// 重 写 add @Override
protected void add(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub
// 将来实际业务中,Colleage 的 add 和 University add 不一定完全一样
organizationComponents.add(organizationComponent);
}
// 重 写 remove @Override
protected void remove(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
// TODO Auto-generated method stub return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub return super.getDes();
}
// print 方法,就是输出 University 包含的学院
@Override
protected void print() {
// TODO Auto-generated method stub
System.out.println("--------------" + getName() + " ");
//遍历 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print();
}
}
}
package com.atguigu.composite;
public class Department extends OrganizationComponent {
//没有集合
public Department(String name, String des) { super(name, des);
// TODO Auto-generated constructor stub
}
//add , remove 就不用写了,因为他是叶子节点
@Override
public String getName() {
// TODO Auto-generated method stub return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub return super.getDes();
}
@Override
protected void print() {
// TODO Auto-generated method stub System.out.println(getName());
}
}
package com.atguigu.composite;
public abstract class OrganizationComponent { private String name; // 名 字
private String des; // 说 明
protected void add(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent) {
//默认实现
throw new UnsupportedOperationException();
}
//构造器
public OrganizationComponent(String name, String des) { super();
this.name = name; this.des = des;
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) { this.des = des;
}
//方法 print, 做成抽象的, 子类都需要实现
protected abstract void print();
}
package com.atguigu.composite;
import java.util.ArrayList; import java.util.List;
//University 就是 Composite , 可以管理 College
public class University extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 构造器
public University(String name, String des) {
super(name, des);
// TODO Auto-generated constructor stub
}
// 重 写 add @Override
protected void add(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub organizationComponents.add(organizationComponent);
}
// 重 写 remove @Override
protected void remove(OrganizationComponent organizationComponent) {
// TODO Auto-generated method stub organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
// TODO Auto-generated method stub return super.getName();
}
@Override
public String getDes() {
// TODO Auto-generated method stub
return super.getDes();
}
// print 方法,就是输出 University 包含的学院
@Override
protected void print() {
// TODO Auto-generated method stub
System.out.println("--------------" + getName() + " ");
//遍历 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print();
}
}
}
JDK应用实例:HashMap
UML类图:
组合模式的注意事项和细节:
-
简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
-
具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
-
方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
-
需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
-
要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式
10.外观模式
传统方式解决影院管理:
外观模式的介绍:
-
外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
-
外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
原理UML类图:
Ø 对类图说明(分类外观模式的角色)
-
外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
-
调用者(Client): 外观接口的调用者
-
子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,他是功能的实际提供者
应用实例:
代码:
package com.atguigu.facade;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//这里直接调用。。 很麻烦
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); homeTheaterFacade.ready();
homeTheaterFacade.play();
homeTheaterFacade.end();
}
}
package com.atguigu.facade;
public class DVDPlayer {
//使用单例模式, 使用饿汉式
private static DVDPlayer instance = new DVDPlayer();
public static DVDPlayer getInstanc() { return instance;
}
public void on() { System.out.println(" dvd on ");
}
public void off() { System.out.println(" dvd off ");
}
public void play() {
System.out.println(" dvd is playing ");
}
//....
public void pause() { System.out.println(" dvd pause ..");
}
}
package com.atguigu.facade;
public class HomeTheaterFacade {
//定义各个子系统对象
private TheaterLight theaterLight; private Popcorn popcorn;
private Stereo stereo; private Projector projector; private Screen screen;
private DVDPlayer dVDPlayer;
//构造器
public HomeTheaterFacade() { super();
this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();
this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getInstanc();
}
//操作分成 4 步
public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim();
}
public void play() { dVDPlayer.play();
}
public void pause() { dVDPlayer.pause();
}
public void end() { popcorn.off();
theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off();
}
}
package com.atguigu.facade;
public class Popcorn {
private static Popcorn instance = new Popcorn();
public static Popcorn getInstance() { return instance;
}
public void on() {
System.out.println(" popcorn on ");
}
public void off() { System.out.println(" popcorn ff ");
}
public void pop() {
System.out.println(" popcorn is poping ");
}
}
package com.atguigu.facade;
public class Projector {
private static Projector instance = new Projector();
public static Projector getInstance() { return instance;
}
public void on() {
System.out.println(" Projector on ");
}
public void off() {
System.out.println(" Projector ff ");
}
public void focus() {
System.out.println(" Projector is Projector ");
}
//...
}
package com.atguigu.facade;
public class Screen {
private static Screen instance = new Screen();
public static Screen getInstance() { return instance;
}
public void up() { System.out.println(" Screen up ");
}
public void down() { System.out.println(" Screen down ");
}
}
package com.atguigu.facade;
public class Stereo {
private static Stereo instance = new Stereo();
public static Stereo getInstance() { return instance;
}
public void on() { System.out.println(" Stereo on ");
}
public void off() { System.out.println(" Screen off ");
}
public void up() {
System.out.println(" Screen up.. ");
}
//...
}
package com.atguigu.facade;
public class TheaterLight {
private static TheaterLight instance = new TheaterLight();
public static TheaterLight getInstance() { return instance;
}
public void on() {
System.out.println(" TheaterLight on ");
}
public void off() {
System.out.println(" TheaterLight off ");
}
public void dim() {
System.out.println(" TheaterLight dim.. ");
}
public void bright() {
System.out.println(" TheaterLight bright.. ");
}
}
外观模式在MyBatis中的应用:
UML类图:
外观模式的注意事项和细节
-
外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
-
外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
-
通过合理的使用外观模式,可以帮我们更好的划分访问的层次
-
当系统需要进行分层设计时,可以考虑使用 Facade 模式
-
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个
Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
- 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
11.享元模式
基本介绍
-
享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
-
常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
-
享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
-
享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
原理UML类图:
Ø 对类图的说明
对原理图的说明-即(模式的角色及职责)
-
FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
-
ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
-
UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
-
FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法
内部状态和外部状态
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态
-
享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
-
内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
-
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
-
举个例子:围棋理论上有 361 个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题
应用实例:
使用享元模式完成,前面提出的网站外包问题
类图:
代码实现:
package com.atguigu.flyweight;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建一个工厂类
WebSiteFactory factory = new WebSiteFactory();
// 客户要一个以新闻形式发布的网站
WebSite webSite1 = factory.getWebSiteCategory("新闻");
webSite1.use(new User("tom"));
// 客户要一个以博客形式发布的网站
WebSite webSite2 = factory.getWebSiteCategory("博客");
webSite2.use(new User("jack"));
// 客户要一个以博客形式发布的网站
WebSite webSite3 = factory.getWebSiteCategory("博客");
webSite3.use(new User("smith"));
// 客户要一个以博客形式发布的网站
WebSite webSite4 = factory.getWebSiteCategory("博客");
webSite4.use(new User("king"));
System.out.println("网站的分类共=" + factory.getWebSiteCount());
}
}
package com.atguigu.flyweight;
//具体网站
public class ConcreteWebSite extends WebSite {
//共享的部分,内部状态
private String type = ""; //网站发布的形式(类型)
//构造器
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
// TODO Auto-generated method stub
System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
}
}
package com.atguigu.flyweight;
public class User {
private String name;
public User(String name) { super();
this.name = name;
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
}
package com.atguigu.flyweight;
public abstract class WebSite {
public abstract void use(User user);//抽象方法
}
package com.atguigu.flyweight;
import java.util.HashMap;
// 网站工厂类,根据需要返回压一个网站
public class WebSiteFactory {
//集合, 充当池的作用
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
public WebSite getWebSiteCategory(String type) { if(!pool.containsKey(type)) {
//就创建一个网站,并放入到池中
pool.put(type, new ConcreteWebSite(type));
}
return (WebSite)pool.get(type);
}
//获取网站分类的总数 (池中有多少个网站类型) public int getWebSiteCount() {
return pool.size();
}
}
享元模式的注意事项和细节
-
在享元模式这样理解,“享”就表示共享,“元”表示对象
-
系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
-
用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
-
享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
-
享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
-
使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
-
享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池
12.代理模式
基本介绍:
-
代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
-
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
-
代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
示意图:
静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
应用实例
Ø 具体要求
-
定义一个接口:ITeacherDao
-
目标对象 TeacherDAO 实现接口 ITeacherDAO
-
使用静态代理方式,就需要在代理对象 TeacherDAOProxy 中也实现 ITeacherDAO
-
调用的时候通过调用代理对象的方法来调用目标对象.
-
特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
Ø 思路分析图解(类图):
代码:
package com.atguigu.proxy.staticproxy;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象(被代理对象)
TeacherDao teacherDao = new TeacherDao();
//创建代理对象, 同时将被代理对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法
teacherDaoProxy.teach();
}
}
package com.atguigu.proxy.staticproxy;
//接口
public interface ITeacherDao {
void teach(); // 授课的方法
}
package com.atguigu.proxy.staticproxy;
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub System.out.println(" 老师授课中 。。。。。");
}
}
package com.atguigu.proxy.staticproxy;
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; // 目标对象,通过接口来聚合
//构造器
public TeacherDaoProxy(ITeacherDao target) { this.target = target;
}
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println("开始代理 完成某些操作。。。。。 ");//方法target.teach();
System.out.println("提交。。。。。");//方法
}
}
-
优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
-
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
-
一旦接口增加方法,目标对象与代理对象都要维护
动态代理
-
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
-
代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
-
动态代理也叫做:JDK 代理、接口代理
UML类图:
代码:
package com.atguigu.proxy.dynamic;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
ITeacherDao target = new TeacherDao();
//给目标对象,创建代理对象, 可以转成 ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
System.out.println("proxyInstance=" + proxyInstance.getClass());
//通过代理对象,调用目标对象的方法
//proxyInstance.teach();
proxyInstance.sayHello(" tom ");
}
}
package com.atguigu.proxy.dynamic;
//接口
public interface ITeacherDao {
void teach(); // 授课方法
void sayHello(String name);
}
package com.atguigu.proxy.dynamic;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
//维护一个目标对象 , Object private Object target;
//构造器 , 对 target 进行初始化
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象 生成一个代理对象
public Object getProxyInstance() {
//说明
/*
* public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行
的目标对象方法作为参数传入
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub System.out.println("JDK 代理开始~~");
//反射机制调用目标对象的方法
Object returnVal = method.invoke(target, args); System.out.println("JDK 代理提交");
return returnVal;
}
});
}
}
package com.atguigu.proxy.dynamic;
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub System.out.println(" 老师授课中 ");
}
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub System.out.println("hello " + name);
}
}
Cglib代理
-
静态代理和JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 **Cglib** 代理
-
Cglib 代理也叫作子类代理**,**它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理。
-
Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
-
在 AOP 编程中如何选择代理模式:
\1. 目标对象需要实现接口,用 JDK 代理
\2. 目标对象不需要实现接口,用 Cglib 代理
-
Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
实现步骤:
需要引入 cglib 的 jar 文件
-
在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
-
目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
Cglib 代理模式应用实例
Ø 应用实例要求
将前面的案例用 Cglib 代理模式实现
思路图解(类图):
代码:
package com.atguigu.proxy.cglib;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
TeacherDao target = new TeacherDao();
//获取到代理对象,并且将目标对象传递给代理对象
TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法,触发 intecept 方法,从而实现 对目标对象的调用
String res = proxyInstance.teach(); System.out.println("res=" + res);
}
}
package com.atguigu.proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory implements MethodInterceptor {
//维护一个目标对象private Object target;
//构造器,传入一个被代理的对象public ProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象: 是 target 对象的代理对象
public Object getProxyInstance() {
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 设置父类
enhancer.setSuperclass(target.getClass());
//3. 设置回调函数
enhancer.setCallback(this);
//4. 创建子类对象,即代理对象
return enhancer.create();
}
//重写 intercept 方法,会调用目标对象的方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub System.out.println("Cglib 代理模式 ~~ 开始"); Object returnVal = method.invoke(target, args); System.out.println("Cglib 代理模式 ~~ 提交"); return returnVal;
}
}
package com.atguigu.proxy.cglib;
public class TeacherDao {
public String teach() {
System.out.println(" 老师授课中 , 我是 cglib 代理,不需要实现接口 "); return "hello";
}
}
13.模版方法模式
基本介绍
-
模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),z 在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
-
简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
-
这种类型的设计模式属于行为型模式。
模板方法模式的原理类图
Ø 对原理类图的说明-即(模板方法模式的角色及职责)
-
AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法 operationr2,3,4
-
ConcreteClass 实现抽象方法 operationr2,3,4, 以完成算法中特点子类的步骤
-
应用实例要求
编写制作豆浆的程序,说明如下:
制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎通过添加不同的配料,可以制作出不同口味的豆浆
选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红豆、花生豆浆。。。)
- 思路分析和图解(类图)
代码:
package com.atguigu.template;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//制作红豆豆浆
System.out.println("----制作红豆豆浆 ");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk(); redBeanSoyaMilk.make();
System.out.println("----制作花生豆浆 ");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
}
}
package com.atguigu.template;
public class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
// TODO Auto-generated method stub System.out.println(" 加入上好的花生 ");
}
}
package com.atguigu.template;
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
// TODO Auto-generated method stub System.out.println(" 加入上好的红豆 ");
}
}
package com.atguigu.template;
//抽象类,表示豆浆
public abstract class SoyaMilk {
//模板方法, make , 模板方法可以做成 final , 不让子类去覆盖. final void make() {
select(); addCondiments(); soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 ");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");
}
}
模板方法模式的钩子方法
-
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
-
还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造
代码:
package com.atguigu.template.improve;
//抽象类,表示豆浆
public abstract class SoyaMilk {
//模板方法, make , 模板方法可以做成 final , 不让子类去覆盖. final void make() {
select(); if(customerWantCondiments()) {
addCondiments();
}
soak();
beat();
}
//选材料
void select() {
System.out.println("第一步:选择好的新鲜黄豆 ");
}
//添加不同的配料, 抽象方法, 子类具体实现
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("第三步, 黄豆和配料开始浸泡, 需要 3 小时 ");
}
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎 ");
}
//钩子方法,决定是否需要添加配料boolean customerWantCondiments() {
return true;
}
}
模板方法模式在Spring 框架应用的源码分析
-
Spring IOC 容器初始化时运用到的模板方法模式
-
代码分析+角色分析+说明类图
- 针对源码的类图(说明层次关系)
模板方法模式的注意事项和细节
-
基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
-
实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
-
既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
-
该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
-
一般模板方法都加上 final 关键字, 防止子类重写模板方法.
-
模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理
14.命令模式
介绍:
-
命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
-
命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
-
在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
-
通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象
原理类图:
Ø 对原理类图的说明-即(命名模式的角色及职责)
-
Invoker 是调用者角色
-
Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
-
Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
-
ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现 execute
项目实例:
-
编写程序,使用命令模式 完成前面的智能家电项目
-
思路分析和图解
代码:
package com.atguigu.command;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用命令设计模式,完成通过遥控器,对电灯的操作
//创建电灯的对象(接受者)
LightReceiver lightReceiver = new LightReceiver();
//创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
//需要一个遥控器
RemoteController remoteController = new RemoteController();
//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("--------按下灯的开按钮 ");
remoteController.onButtonWasPushed(0); System.out.println("--------按下灯的关按钮 ");
remoteController.offButtonWasPushed(0);
System.out.println("--------按下撤销按钮 ");
remoteController.undoButtonWasPushed();
System.out.println("=========使用遥控器操作电视机=========="); TVReceiver tvReceiver = new TVReceiver();
TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver); TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver);
//给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作
remoteController.setCommand(1, tvOnCommand, tvOffCommand);
System.out.println("--------按下电视机的开按钮 ");
remoteController.onButtonWasPushed(1); System.out.println("--------按下电视机的关按钮 ");
remoteController.offButtonWasPushed(1);
System.out.println("--------按下撤销按钮 ");
remoteController.undoButtonWasPushed();
}
}
package com.atguigu.command;
//创建命令接口
public interface Command {
//执行动作(操作) public void execute();
//撤销动作(操作) public void undo();
}
package com.atguigu.command;
public class LightOffCommand implements Command {
// 聚 合 LightReceiver LightReceiver light;
// 构造器
public LightOffCommand(LightReceiver light) { super();
this.light = light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
light.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
light.on();
}
}
package com.atguigu.command;
public class LightOnCommand implements Command {
//聚合 LightReceiver LightReceiver light;
//构造器
public LightOnCommand(LightReceiver light) { super();
this.light = light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
//调用接收者的方法light.on();
}
@Override
public void undo() {
// TODO Auto-generated method stub
//调用接收者的方法
light.off();
}
}
package com.atguigu.command;
public class LightReceiver {
public void on() {
System.out.println(" 电灯打开了.. ");
}
public void off() {
System.out.println(" 电灯关闭了.. ");
}
}
package com.atguigu.command;
/**
* 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
* 其实,这样是一种设计模式, 可以省掉对空判断
* @author Administrator*
*/
public class NoCommand implements Command {
@Override
public void execute() {
// TODO Auto-generated method stub
}
@Override
public void undo() {
// TODO Auto-generated method stub
}
}
package com.atguigu.command;
public class RemoteController {
// 开 按钮的命令数组
Command[] onCommands; Command[] offCommands;
// 执行撤销的命令
Command undoCommand;
// 构造器,完成对按钮初始化
public RemoteController() {
onCommands = new Command[5]; offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand();
}
}
// 给我们的按钮设置你需要的命令
public void setCommand(int no, Command onCommand, Command offCommand) { onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开按钮
public void onButtonWasPushed(int no) { // no 0
// 找到你按下的开的按钮, 并调用对应方法
onCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = onCommands[no];
}
// 按下开按钮
public void offButtonWasPushed(int no) { // no 0
// 找到你按下的关的按钮, 并调用对应方法
offCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
package com.atguigu.command;
public class TVOffCommand implements Command {
// 聚 合 TVReceiver
TVReceiver tv;
// 构造器
public TVOffCommand(TVReceiver tv) {
super();
this.tv = tv;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.on();
}
}
package com.atguigu.command;
public class TVOnCommand implements Command {
// 聚 合 TVReceiver TVReceiver tv;
// 构造器
public TVOnCommand(TVReceiver tv) { super();
this.tv = tv;
}
@Override
public void execute() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.on();
}
@Override
public void undo() {
// TODO Auto-generated method stub
// 调用接收者的方法
tv.off();
}
}
package com.atguigu.command;
public class TVReceiver {
public void on() {
System.out.println(" 电视机打开了.. ");
}
public void off() {
System.out.println(" 电视机关闭了.. ");
}
}
命令模式在 Spring 框架 JdbcTemplate 应用的源码分析
-
Spring 框架的 JdbcTemplate 就使用到了命令模式
代码分析
-
模式角色分析说明
l StatementCallback 接口 ,类似命令接口(Command)
l class QueryStatementCallback implements StatementCallback, SqlProvider , 匿名内部类, 实现了命令接口, 同时也充当命令接收者
l 命令调用者 是 JdbcTemplate , 其中 execute(StatementCallback action) 方法中,调用 action.doInStatement 方法. 不同的 实现 StatementCallback 接口的对象,对应不同的 doInStatemnt 实现逻辑
l 另外实现 StatementCallback 命令接口的子类还有 QueryStatementCallback、
命令模式的注意事项和细节
-
将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了 纽带桥梁的作用。
-
容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
-
容易实现对请求的撤销和重做
-
命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
-
空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
-
命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
15.访问者模式
访问者模式基本介绍:
-
访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
-
主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
-
访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
-
访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
原理类图:
Ø 对原理类图的说明-
即(访问者模式的角色及职责)
-
Visitor 是抽象访问者,为该对象结构中的 ConcreteElement 的每一个类声明一个 visit 操作
-
ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分.
-
ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
-
Element 定义一个 accept 方法,接收一个访问者对象
-
ConcreteElement 为具体元素,实现了 accept 方法
应用实例要求
-
将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等),请使用访问者模式来说实现
-
思路分析和图解(类图):
代码:
package com.atguigu.visitor;
public abstract class Action {
//得到男性 的测评
public abstract void getManResult(Man man);
//得到女的 测评
public abstract void getWomanResult(Woman woman);
}
package com.atguigu.visitor;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建 ObjectStructure
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man()); objectStructure.attach(new Woman());
//成功
Success success = new Success(); objectStructure.display(success);
System.out.println("==============="); Fail fail = new Fail(); objectStructure.display(fail);
System.out.println("=======给的是待定的测评========");
Wait wait = new Wait(); objectStructure.display(wait);
}
}
package com.atguigu.visitor;
public class Fail extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub System.out.println(" 男人给的评价该歌手失败 !");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub System.out.println(" 女人给的评价该歌手失败 !");
}
}
package com.atguigu.visitor;
public class Man extends Person {
@Override
public void accept(Action action) {
// TODO Auto-generated method stub action.getManResult(this);
}
}
package com.atguigu.visitor;
import java.util.LinkedList; import java.util.List;
//数据结构,管理很多人(Man , Woman) public class ObjectStructure {
//维护了一个集合
private List<Person> persons = new LinkedList<>();
//增加到 list
public void attach(Person p) {
persons.add(p);
}
//移除
public void detach(Person p) { persons.remove(p);
}
//显示测评情况
public void display(Action action) { for(Person p: persons) {
p.accept(action);
}
}
}
package com.atguigu.visitor;
public abstract class Person {
//提供一个方法,让访问者可以访问public abstract void accept(Action action);
}
package com.atguigu.visitor;
public class Success extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub System.out.println(" 男人给的评价该歌手很成功 !");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub System.out.println(" 女人给的评价该歌手很成功 !");
}
}
package com.atguigu.visitor;
public class Wait extends Action {
@Override
public void getManResult(Man man) {
// TODO Auto-generated method stub System.out.println(" 男人给的评价是该歌手待定 ..");
}
@Override
public void getWomanResult(Woman woman) {
// TODO Auto-generated method stub System.out.println(" 女人给的评价是该歌手待定 ..");
}
}
package com.atguigu.visitor;
//说明
//1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递 Woman 中(第一次分派)
//2. 然后 Woman 类调用作为参数的 "具体方法" 中方法 getWomanResult, 同时将自己(this)作为参数
// 传入,完成第二次的分派
public class Woman extends Person{
@Override
public void accept(Action action) {
// TODO Auto-generated method stub action.getWomanResult(this);
}
}
-
应用案例的小结-双分派
-上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
- 以上述实例为例,假设我们要添加一个 Wait 的状态类,考察 Man 类和 Woman 类的反应,由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码。
访问者模式的注意事项和细节
Ø 优点
-
访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
-
访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
Ø 缺点
-
具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
-
违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
-
因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.
16.迭代器模式
-
迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
-
如果我们的集合元素是用不同的方式实现的,有数组,还有 java 的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
-
迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。
代器模式的原理类图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200417101838620.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMzgwNzM0,size_16,color_FFFFFF,t_70)
Ø 对原理类图的说明-即(迭代器模式的角色及职责)
-
Iterator : 迭代器接口,是系统提供,含义 hasNext, next, remove
-
ConcreteIterator : 具体的迭代器类,管理迭代
-
Aggregate :一个统一的聚合接口, 将客户端和具体聚合解耦
-
ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历集合
-
Client :客户端, 通过 Iterator 和 Aggregate 依赖子类
应用实例要求
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。
设计思路分析
代码:
package com.atguigu.iterator;
import java.util.ArrayList; import java.util.List;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建学院
List<College> collegeList = new ArrayList<College>();
ComputerCollege computerCollege = new ComputerCollege(); InfoCollege infoCollege = new InfoCollege();
collegeList.add(computerCollege);
//collegeList.add(infoCollege);
OutPutImpl outPutImpl = new OutPutImpl(collegeList); outPutImpl.printCollege();
}
}
package com.atguigu.iterator;
import java.util.Iterator;
public interface College {
public String getName();
//增加系的方法
public void addDepartment(String name, String desc);
//返回一个迭代器,遍历
public Iterator createIterator();
}
package com.atguigu.iterator;
import java.util.Iterator;
public class ComputerCollege implements College {
Department[] departments;
int numOfDepartment = 0 ;// 保存当前数组的对象个数
public ComputerCollege() { departments = new Department[5];
addDepartment("Java 专业", " Java 专业 ");
addDepartment("PHP 专 业 ", " PHP 专 业 "); addDepartment("大数据专业", " 大数据专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub return "计算机学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc); departments[numOfDepartment] = department; numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new ComputerCollegeIterator(departments);
}
}
package com.atguigu.iterator;
import java.util.Iterator;
public class ComputerCollegeIterator implements Iterator {
//这里我们需要 Department 是以怎样的方式存放=>数组
Department[] departments;
int position = 0; //遍历的位置
public ComputerCollegeIterator(Department[] departments) { this.departments = departments;
}
//判断是否还有下一个元素@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(position >= departments.length || departments[position] == null) { return false;
}else {
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub Department department = departments[position]; position += 1;
return department;
}
//删除的方法,默认空实现public void remove() {
}
}
package com.atguigu.iterator;
//系
public class Department {
private String name; private String desc;
public Department(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
package com.atguigu.iterator;
import java.util.Iterator; import java.util.List;
public class InfoColleageIterator implements Iterator {
List<Department> departmentList; // 信息工程学院是以 List 方式存放系
int index = -1;//索引
public InfoColleageIterator(List<Department> departmentList) { this.departmentList = departmentList;
}
//判断 list 中还有没有下一个元素
@Override
public boolean hasNext() {
// TODO Auto-generated method stub if(index >= departmentList.size() - 1) {
return false;
} else {
index += 1; return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub return departmentList.get(index);
}
// 空 实 现 remove public void remove() {
}
}
package com.atguigu.iterator;
import java.util.ArrayList; import java.util.Iterator; import java.util.List;
public class InfoCollege implements College {
List<Department> departmentList;
public InfoCollege() {
departmentList = new ArrayList<Department>();
addDepartment("信息安全专业", " 信息安全专业 "); addDepartment("网络安全专业", " 网络安全专业 "); addDepartment("服务器安全专业", " 服务器安全专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub return "信息工程学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc); departmentList.add(department);
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new InfoColleageIterator(departmentList);
}
}
package com.atguigu.iterator;
import java.util.Iterator; import java.util.List;
public class OutPutImpl {
//学院集合
List<College> collegeList;
public OutPutImpl(List<College> collegeList) {
this.collegeList = collegeList;
}
//遍历所有学院,然后调用 printDepartment 输出各个学院的系
public void printCollege() {
//从 collegeList 取出所有学院, Java 中的 List 已经实现 Iterator Iterator<College> iterator = collegeList.iterator();
while(iterator.hasNext()) {
//取出一个学院
College college = iterator.next();
System.out.println("=== "+college.getName() +"=====" ); printDepartment(college.createIterator()); //得到对应迭代器
}
}
//输出 学院输出 系
public void printDepartment(Iterator iterator) { while(iterator.hasNext()) {
Department d = (Department)iterator.next(); System.out.println(d.getName());
}
}
}
迭代器模式在JDK-ArrayList 集合应用的源码分析
-
JDK 的 ArrayList 集合中就使用了迭代器模式
-
代码分析+类图+说明
- 对类图的角色分析和说明
ü 内部类 Itr 充当具体实现迭代器 Iterator 的类, 作为 ArrayList 内部类
ü List 就是充当了聚合接口,含有一个 iterator() 方法,返回一个迭代器对象
ü ArrayList 是实现聚合接口 List 的子类,实现了 iterator()
ü Iterator 接口系统提供
ü 迭代器模式解决了 不同集合(ArrayList ,LinkedList) 统一遍历问题
迭代器模式的注意事项和细节
Ø 优点
-
提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
-
隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
-
提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
-
当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
Ø 缺点
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类
17.观察者模式
观察者模式原理
-
观察者模式类似订牛奶业务
-
奶站/气象局:Subject
-
用户/第三方网站:Observer
Ø Subject:登记注册、移除和通知
-
registerObserver 注 册
-
removeObserver 移 除
-
notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送, 看具体需求定
Ø Observer:接收输入
Ø 观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject
通知 Observer 变化,比如这里的奶站是 Subject,是 1 的一方。用户时 Observer,是多的一方。
观察者模式解决天气预报需求
类图:
代码:
package com.atguigu.observer.improve;
public class BaiduSite implements Observer {
// 温度,气压,湿度private float temperature; private float pressure; private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure; this.humidity = humidity; display();
}
// 显 示
public void display() { System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***"); System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
package com.atguigu.observer.improve;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个 WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
BaiduSite baiduSite = new BaiduSite();
// 注 册 到 weatherData weatherData.registerObserver(currentConditions); weatherData.registerObserver(baiduSite);
//测试
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
weatherData.removeObserver(currentConditions);
//测试System.out.println();
System.out.println("通知各个注册的观察者, 看看信息"); weatherData.setData(10f, 100f, 30.3f);
}
}
package com.atguigu.observer.improve;
public class CurrentConditions implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure; private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
public void update(float temperature, float pressure, float humidity) { this.temperature = temperature;
this.pressure = pressure; this.humidity = humidity; display();
}
// 显 示
public void display() {
System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***");
}
}
package com.atguigu.observer.improve;
//观察者接口,有观察者来实现public interface Observer {
public void update(float temperature, float pressure, float humidity);
}
package com.atguigu.observer.improve;
//接口, 让 WeatherData 来实现
public interface Subject {
public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers();
}
package com.atguigu.observer.improve;
import java.util.ArrayList;
/**
* 类是核心
* 1. 包含最新的天气情况信息
* 2. 含有 观察者集合,使用 ArrayList 管理
* 3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息
* @author Administrator
*
*/
public class WeatherData implements Subject { private float temperatrue;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
//加入新的第三方public WeatherData() {
observers = new ArrayList<Observer>();
}
public float getTemperature() { return temperatrue;
}
public float getPressure() { return pressure;
}
public float getHumidity() { return humidity;
}
public void dataChange() {
//调用 接入方的 update
notifyObservers();
}
//当数据有更新时,就调用 setData
public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature;
this.pressure = pressure; this.humidity = humidity;
//调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
dataChange();
}
//注册一个观察者@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub observers.add(o);
}
//移除一个观察者@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub if(observers.contains(o)) {
observers.remove(o);
}
}
观察者模式的好处
-
观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
-
这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。
观察者模式在Jdk 应用的源码分析
-
Jdk 的 Observable 类就使用了观察者模式
-
代码分析+模式角色分析
- 模式角色分析
ü Observable 的作用和地位等价于 我们前面讲过 Subject
ü Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add… delete … notify…
ü Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
ü Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
18.中介者模式
基本介绍
-
中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
-
中介者模式属于行为型模式,使代码易于维护
-
比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用
中介者模式的原理类图
Ø 对原理类图的说明-即(中介者模式的角色及职责)
-
Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
-
Colleague 是抽象同事类
-
ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理
HashMap,并接受某个同事对象消息,完成相应的任务
- ConcreteColleague 具体的同事类,会有很多, 每个同事只知道自己的行为, 而不了解其他同事类的行为(方法), 但 是他们都依赖中介者对象
中介者模式应用实例-智能家庭管理
- 应用实例要求
完成前面的智能家庭的项目,使用中介者模式
-
思路分析和图解(类图)、
代码:
package com.atguigu.mediator.smarthouse;
//具体的同事类
public class Alarm extends Colleague {
//构造器
public Alarm(Mediator mediator, String name) {
super(mediator, name);
// TODO Auto-generated constructor stub
//在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合] mediator.Register(name, this);
}
public void SendAlarm(int stateChange) { SendMessage(stateChange);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub
// 调 用 的 中 介 者 对 象 的 getMessage this.GetMediator().GetMessage(stateChange, this.name);
}
}
package com.atguigu.mediator.smarthouse;
public class ClientTest {
public static void main(String[] args) {
//创建一个中介者对象
Mediator mediator = new ConcreteMediator();
//创建 Alarm 并且加入到 ConcreteMediator 对象的 HashMap Alarm alarm = new Alarm(mediator, "alarm");
//创建了 CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的 HashMap CoffeeMachine coffeeMachine = new CoffeeMachine(mediator,
"coffeeMachine");
//创建 Curtains , 并 且加入到 ConcreteMediator 对象的 HashMap Curtains curtains = new Curtains(mediator, "curtains");
TV tV = new TV(mediator, "TV");
//让闹钟发出消息alarm.SendAlarm(0); coffeeMachine.FinishCoffee(); alarm.SendAlarm(1);
}
}
package com.atguigu.mediator.smarthouse;
public class CoffeeMachine extends Colleague {
public CoffeeMachine(Mediator mediator, String name) { super(mediator, name);
// TODO Auto-generated constructor stub mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name);
}
public void StartCoffee() { System.out.println("It's time to startcoffee!");
}
public void FinishCoffee() {
System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); SendMessage(0);
}
}
package com.atguigu.mediator.smarthouse;
//同事抽象类
public abstract class Colleague {
private Mediator mediator; public String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator; this.name = name;
}
public Mediator GetMediator() { return this.mediator;
}
public abstract void SendMessage(int stateChange);
}
package com.atguigu.mediator.smarthouse;
import java.util.HashMap;
//具体的中介者类
public class ConcreteMediator extends Mediator {
//集合,放入所有的同事对象
private HashMap<String, Colleague> colleagueMap;
private HashMap<String, String> interMap;
public ConcreteMediator() {
colleagueMap = new HashMap<String, Colleague>(); interMap = new HashMap<String, String>();
}
@Override
public void Register(String colleagueName, Colleague colleague) {
// TODO Auto-generated method stub colleagueMap.put(colleagueName, colleague);
// TODO Auto-generated method stub
if (colleague instanceof Alarm) { interMap.put("Alarm", colleagueName);
} else if (colleague instanceof CoffeeMachine) { interMap.put("CoffeeMachine", colleagueName);
} else if (colleague instanceof TV) { interMap.put("TV", colleagueName);
} else if (colleague instanceof Curtains) { interMap.put("Curtains", colleagueName);
}
}
//具体中介者的核心方法
//1. 根据得到消息,完成对应任务
//2. 中介者在这个方法,协调各个具体的同事对象,完成任务
@Override
public void GetMessage(int stateChange, String colleagueName) {
// TODO Auto-generated method stub
//处理闹钟发出的消息
if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) {
((CoffeeMachine) (colleagueMap.get(interMap
.get("CoffeeMachine")))).StartCoffee();
((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
} else if (stateChange == 1) {
((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
}
} else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { ((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
.UpCurtains();
} else if (colleagueMap.get(colleagueName) instanceof TV) {//如果 TV 发现消息
} else if (colleagueMap.get(colleagueName) instanceof Curtains) {
//如果是以窗帘发出的消息,这里处理...
}
}
@Override
public void SendMessage() {
// TODO Auto-generated method stub
}
}
package com.atguigu.mediator.smarthouse;
public class Curtains extends Colleague {
public Curtains(Mediator mediator, String name) { super(mediator, name);
// TODO Auto-generated constructor stub mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name);
}
public void UpCurtains() {
System.out.println("I am holding Up Curtains!");
}
}
package com.atguigu.mediator.smarthouse;
public abstract class Mediator {
//将给中介者对象,加入到集合中
public abstract void Register(String colleagueName, Colleague colleague);
//接收消息, 具体的同事对象发出
public abstract void GetMessage(int stateChange, String colleagueName);
public abstract void SendMessage();
}
package com.atguigu.mediator.smarthouse;
public class TV extends Colleague {
public TV(Mediator mediator, String name) { super(mediator, name);
// TODO Auto-generated constructor stub
mediator.Register(name, this);
}
@Override
public void SendMessage(int stateChange) {
// TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name);
}
public void StartTv() {
// TODO Auto-generated method stub System.out.println("It's time to StartTv!");
}
public void StopTv() {
// TODO Auto-generated method stub System.out.println("StopTv!");
}
}
中介者模式的注意事项和细节
-
多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
-
减少类间依赖,降低了耦合,符合迪米特原则
-
中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
-
如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
19.备忘录模式
基本介绍
-
备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
-
可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
-
备忘录模式属于行为型模式
备忘录模式的原理类图
Ø 对原理类图的说明-即(备忘录模式的角色及职责)
-
originator : 对象(需要保存状态的对象)
-
Memento : 备忘录对象,负责保存好记录,即 Originator 内部状态
-
Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
-
说明:如果希望保存多个 originator 对象的不同时间的状态,也可以,只需要要 HashMap <String, 集合>
代码:
package com.atguigu.memento.theory;
import java.util.ArrayList; import java.util.List;
public class Caretaker {
//在 List 集合中会有很多的备忘录对象
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento memento) { mementoList.add(memento);
}
//获取到第 index 个 Originator 的 备忘录对象(即保存状态) public Memento get(int index) {
return mementoList.get(index);
}
}
package com.atguigu.memento.theory;
import java.util.ArrayList; import java.util.HashMap;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Originator originator = new Originator(); Caretaker caretaker = new Caretaker();
originator.setState(" 状态#1 攻击力 100 ");
//保存了当前的状态caretaker.add(originator.saveStateMemento());
originator.setState(" 状 态 #2 攻 击 力 80 "); caretaker.add(originator.saveStateMemento());
originator.setState(" 状 态 #3 攻 击 力 50 "); caretaker.add(originator.saveStateMemento());
System.out.println("当前的状态是 =" + originator.getState());
//希望得到状态 1, 将 originator 恢复到状态 1
originator.getStateFromMemento(caretaker.get(0)); System.out.println("恢复到状态 1 , 当前的状态是");
System.out.println("当前的状态是 =" + originator.getState());
}
}
package com.atguigu.memento.theory;
public class Memento { private String state;
//构造器
public Memento(String state) { super();
this.state = state;
}
public String getState() { return state;
}
}
package com.atguigu.memento.theory;
public class Originator {
private String state;//状态信息public String getState() {
return state;
}
public void setState(String state) { this.state = state;
}
//编写一个方法,可以保存一个状态对象 Memento
//因此编写一个方法,返回 Memento
public Memento saveStateMemento() { return new Memento(state);
}
//通过备忘录对象,恢复状态
public void getStateFromMemento(Memento memento) { state = memento.getState();
}
}
游戏角色恢复状态实例
- 应用实例要求
游戏角色有攻击力和防御力,在大战 Boss 前保存自身的状态(攻击力和防御力),当大战 Boss 后攻击力和防御力下降,从备忘录对象恢复到大战前的状态
- 思路分析和图解(类图)
代码:
package com.atguigu.memento.game;
import java.util.ArrayList; import java.util.HashMap;
//守护者对象, 保存游戏角色的状态
public class Caretaker {
//如果只保存一次状态private Memento memento;
//对 GameRole 保存多次状态
//private ArrayList<Memento> mementos;
//对多个游戏角色保存多个状态
//private HashMap<String, ArrayList<Memento>> rolesMementos;
public Memento getMemento() { return memento;
}
public void setMemento(Memento memento) { this.memento = memento;
}
}
package com.atguigu.memento.game;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建游戏角色
GameRole gameRole = new GameRole(); gameRole.setVit(100); gameRole.setDef(100);
System.out.println("和 boss 大战前的状态"); gameRole.display();
//把当前状态保存 caretaker
Caretaker caretaker = new Caretaker(); caretaker.setMemento(gameRole.createMemento());
System.out.println("和 boss 大战~~~"); gameRole.setDef(30); gameRole.setVit(30);
gameRole.display();
System.out.println("大战后,使用备忘录对象恢复到站前");
gameRole.recoverGameRoleFromMemento(caretaker.getMemento()); System.out.println("恢复后的状态");
gameRole.display();
}
}
package com.atguigu.memento.game;
public class GameRole {
private int vit; private int def;
//创建 Memento ,即根据当前的状态得到 Memento public Memento createMemento() {
return new Memento(vit, def);
}
//从备忘录对象,恢复 GameRole 的状态
public void recoverGameRoleFromMemento(Memento memento) { this.vit = memento.getVit();
this.def = memento.getDef();
}
//显示当前游戏角色的状态public void display() {
System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) { this.vit = vit;
}
public int getDef() { return def;
}
public void setDef(int def) { this.def = def;
}
}
package com.atguigu.memento.game;
public class Memento {
//攻击力private int vit;
//防御力private int def;
public Memento(int vit, int def) { super();
this.vit = vit; this.def = def;
}
public int getVit() { return vit;
}
public void setVit(int vit) { this.vit = vit;
}
public int getDef() { return def;
}
public void setDef(int def) { this.def = def;
}
}
备忘录模式的注意事项和细节
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
-
实现了信息的封装,使得用户不需要关心状态的保存细节
-
如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
-
适用的应用场景:1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理
-
为了节约内存,备忘录模式可以和原型模式配合使用
20.解释器模式
四则运算问题
通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求
-
先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复
-
在分别输入 a ,b, c, d, e 的值
-
最后求出结果:如图
基本介绍
-
在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
-
解释器模式(Interpreter Pattern):是指给定一个语言**(表达式),定义它的文法的一种表示,并定义一个解释器, 使用该解释器来解释语言中的句子(**表达式)
-
应用场景
-应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
-一些重复出现的问题可以用一种简单的语言来表达
-一个简单语法需要解释的场景
- 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等
解释器模式的原理类图
Ø 对原理类图的说明-即(解释器模式的角色及职责)
-
Context: 是环境角色,含有解释器之外的全局信息.
-
AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
-
TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
-
NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作.
-
说明: 输入 Context he TerminalExpression 信息通过 Client 输入即可
解释器模式来实现四则
- 应用实例要求
通过解释器模式来实现四则运算, 如计算 a+b-c 的值
- 思路分析和图解(类图)
代码:
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 加法解释器
* @author Administrator
*
*/
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) { super(left, right);
}
//处理相加
//var 仍然是 {a=10,b=20}..
//一会我们 debug 源码,就 ok
public int interpreter(HashMap<String, Integer> var) {
//super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
//super.right.interpreter(var): 返回 right 表达式对应值 b = 20
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
package com.atguigu.interpreter;
import java.util.HashMap; import java.util.Stack;
public class Calculator {
// 定义表达式
private Expression expression;
// 构造函数传参,并解析
public Calculator(String expStr) { // expStr = a+b
// 安排运算先后顺序
Stack<Expression> stack = new Stack<>();
// 表达式拆分成字符数组
char[] charArray = expStr.toCharArray();// [a, +, b]
Expression left = null; Expression right = null;
//遍历我们的字符数组, 即遍历 [a, +, b]
//针对不同的情况,做处理
for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) {
case '+': //
left = stack.pop();// 从 stack 取 出 left => "a"
right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
stack.push(new AddExpression(left, right));// 然后根据得到 left 和 right 构建 AddExpresson 加入
stack
break; case '-': //
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right));
break; default:
//如果是一个 Var 就创建要给 VarExpression 对象,并 push 到 stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
//当遍历完整个 charArray 数组后,stack 就得到最后 Expression this.expression = stack.pop();
}
public int run(HashMap<String, Integer> var) {
//最后将表达式 a+b 和 var = {a=10,b=20}
//然后传递给 expression 的 interpreter 进行解释执行
return this.expression.interpreter(var);
}
}
package com.atguigu.interpreter;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap;
public class ClientTest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub String expStr = getExpStr(); // a+b
HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
}
// 获得表达式
public static String getExpStr() throws IOException { System.out.print("请输入表达式:");
return (new BufferedReader(new InputStreamReader(System.in))).readLine();
}
// 获得值映射
public static HashMap<String, Integer> getValue(String expStr) throws IOException { HashMap<String, Integer> map = new HashMap<>();
for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
System.out.print("请输入" + String.valueOf(ch) + "的值:");
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in));
}
}
}
return map;
}
}
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 抽象类表达式,通过 HashMap 键值对, 可以获取到变量的值
*
* @author Administrator
*
*/
public abstract class Expression {
// a + b - c
// 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value 就是就是具体值
// HashMap {a=10, b=20}
public abstract int interpreter(HashMap<String, Integer> var);
}
package com.atguigu.interpreter;
import java.util.HashMap;
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//求出 left 和 right 表达式相减后的结果
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
* 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是 Expression 类的实现类
*
* @author Administrator
*
*/
public class SymbolExpression extends Expression {
protected Expression left; protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left; this.right = right;
}
//因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
@Override
public int interpreter(HashMap<String, Integer> var) {
// TODO Auto-generated method stub return 0;
}
}
package com.atguigu.interpreter;
import java.util.HashMap;
/**
* 变量的解释器
* @author Administrator
*
*/
public class VarExpression extends Expression {
private String key; // key=a,key=b,key=c
public VarExpression(String key) { this.key = key;
}
// var 就是{a=10, b=20}
// interpreter 根据 变量名称,返回对应值
@Override
public int interpreter(HashMap<String, Integer> var) { return var.get(this.key);
}
}
解释器模式在Spring 框架应用的源码剖析
-
Spring 框架中 SpelExpressionParser 就使用到解释器模式
-
代码分析+Debug 源码
- 说明
解释器模式的注意事项和细节
-
当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
-
应用场景:编译器、运算表达式计算、正则表达式、机器人等
-
使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.
21.策略模式
策略模式基本介绍
-
策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
-
这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
模式的原理类图
说明:从上图可以看到,客户 context 有成员变量 strategy 或者其他的策略接口
,至于需要使用到哪个策略,我们可以在构造器中指定
策略模式解决鸭子问题
- 应用实例要求
编写程序完成前面的鸭子项目,要求使用策略模式
- 思路分析(类图)
策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
代码:
package com.atguigu.strategy.improve;
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub System.out.println(" 飞翔技术一般 ");
}
}
package com.atguigu.strategy.improve;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub WildDuck wildDuck = new WildDuck(); wildDuck.fly();//
ToyDuck toyDuck = new ToyDuck(); toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck(); pekingDuck.fly();
//动态改变某个对象的行为, 北京鸭 不能飞
pekingDuck.setFlyBehavior(new NoFlyBehavior());
System.out.println("北京鸭的实际飞翔能力");
pekingDuck.fly();
}
}
package com.atguigu.strategy.improve;
public abstract class Duck {
//属性, 策略接口
FlyBehavior flyBehavior;
//其它属性<->策略接口QuackBehavior quackBehavior;
public Duck() {
}
public abstract void display();//显示鸭子信息public void quack() {
System.out.println("鸭子嘎嘎叫~~");
}
public void swim() {
System.out.println("鸭子会游泳~~");
}
public void fly() {
//改进
if(flyBehavior != null) {
flyBehavior.fly();
}
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
package com.atguigu.strategy.improve;
public interface FlyBehavior {
void fly(); // 子类具体实现
}
package com.atguigu.strategy.improve;
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub System.out.println(" 飞翔技术高超 ~~~");
}
}
package com.atguigu.strategy.improve;
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
// TODO Auto-generated method stub System.out.println(" 不会飞翔 ");
}
}
package com.atguigu.strategy.improve;
public class PekingDuck extends Duck {
//假如北京鸭可以飞翔,但是飞翔技术一般public PekingDuck() {
// TODO Auto-generated constructor stub flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub System.out.println("~~北京鸭~~~");
}
}
package com.atguigu.strategy.improve;
public interface QuackBehavior { void quack();//子类实现
}
package com.atguigu.strategy.improve;
public class ToyDuck extends Duck{
public ToyDuck() {
// TODO Auto-generated constructor stub flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub System.out.println("玩具鸭");
}
//需要重写父类的所有方法public void quack() {
System.out.println("玩具鸭不能叫~~");
}
public void swim() {
System.out.println("玩具鸭不会游泳~~");
}
}
package com.atguigu.strategy.improve;
public class WildDuck extends Duck {
//构造器,传入 FlyBehavor 的对象
public WildDuck() {
// TODO Auto-generated constructor stub flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
// TODO Auto-generated method stub System.out.println(" 这是野鸭 ");
}
}
策略模式在 JDK-Arrays 应用的源码分析
- JDK 的 Arrays 的 Comparator 就使用了策略模式
代码分析+Debug 源码
package com.atguigu.jdk;
import java.util.Arrays; import java.util.Comparator;
public class Strategy {
public static void main(String[] args) {
// TODO Auto-generated method stub
//数组
Integer[] data = { 9, 1, 2, 8, 4, 3 };
// 实现降序排序,返回-1 放左边,1 放右边,0 保持不变
// 说 明
// 1. 实现了 Comparator 接口(策略接口) , 匿名类 对象 new Comparator<Integer>(){..}
// 2. 对象 new Comparator<Integer>(){..} 就是实现了 策略接口 的对象
// 3. public int compare(Integer o1, Integer o2){} 指定具体的处理方式
Comparator<Integer> comparator = new Comparator<Integer>() { public int compare(Integer o1, Integer o2) {
if (o1 > o2) {
return -1;
} else {
return 1;
}
};
};
// 说 明
/*
* public static <T> void sort(T[] a, Comparator<? super T> c) { if (c == null) {
sort(a); //默认方法
} else {
if (LegacyMergeSort.userRequested) legacyMergeSort(a, c); //使用策略对象 c
else
// 使用策略对象 c
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
*/
//方式 1
Arrays.sort(data, comparator); System.out.println(Arrays.toString(data)); // 降序排序
//方式 2- 同时 lambda 表达式实现 策略模式
Integer[] data2 = { 19, 11, 12, 18, 14, 13 };
Arrays.sort(data2, (var1, var2) -> { if(var1.compareTo(var2) > 0) {
return -1;
} else {
return 1;
}
});
System.out.println("data2=" + Arrays.toString(data2));
}
}
策略模式的注意事项和细节
-
策略模式的关键是:分析项目中变化部分与不变部分
-
策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
-
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
-
提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy 类中使得你可以独立于其Context 改变它,使它易于切换、易于理解、易于扩展
-
需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
22.职责链模式
职责链模式基本介绍
基本介绍
-
职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
这种类型的设计模式属于行为型模式
职责链模式的原理类图
Ø 对原理类图的说明-即(职责链模式的角色及职责)
-
Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
-
ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
-
Request , 含义很多属性,表示一个请求
职责链模式解决OA 系统采购审批
- 应用实例要求
编写程序完成学校 OA 系统的采购审批项目:需求采购员采购教学器材
如果金额 小于等于 5000, 由教学主任审批如果金额 小于等于 10000, 由院长审批
如果金额 小于等于 30000, 由副校长审批如果金额 超过 30000 以上,有校长审批
- 思路分析和图解(类图)
代码:
package com.atguigu.responsibilitychain;
public abstract class Approver {
Approver approver; //下一个处理者String name; // 名 字
public Approver(String name) {
// TODO Auto-generated constructor stub this.name = name;
}
//下一个处理者
public void setApprover(Approver approver) { this.approver = approver;
}
//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
package com.atguigu.responsibilitychain;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
//创建相关的审批人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任"); CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校"); SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");
//需要将各个审批级别的下一个设置好 (处理人构成环形: ) departmentApprover.setApprover(collegeApprover); collegeApprover.setApprover(viceSchoolMasterApprover); viceSchoolMasterApprover.setApprover(schoolMasterApprover); schoolMasterApprover.setApprover(departmentApprover);
departmentApprover.processRequest(purchaseRequest); viceSchoolMasterApprover.processRequest(purchaseRequest);
}
}
package com.atguigu.responsibilitychain;
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
public class DepartmentApprover extends Approver {
public DepartmentApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub if(purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
//请求类
public class PurchaseRequest {
private int type = 0; //请求类型private float price = 0.0f; //请求金额
private int id = 0;
//构造器
public PurchaseRequest(int type, float price, int id) { this.type = type;
this.price = price;
this.id = id;
}
public int getType() { return type;
}
public float getPrice() { return price;
}
public int getId() { return id;
}
}
package com.atguigu.responsibilitychain;
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
public class ViceSchoolMasterApprover extends Approver {
public ViceSchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
职责链模式在SpringMVC 框架应用的源码分析
-
SpringMVC-HandlerExecutionChain 类就使用到职责链模式
-
SpringMVC 请求流程简图
-
代码分析+Debug 源码+说明
- 对源码总结
Ø springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
Ø 在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
Ø HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
Ø HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.
职责链模式的注意事项和细节
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat
又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
这种类型的设计模式属于行为型模式
职责链模式的原理类图
Ø 对原理类图的说明-即(职责链模式的角色及职责)
-
Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
-
ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
-
Request , 含义很多属性,表示一个请求
职责链模式解决OA 系统采购审批
- 应用实例要求
编写程序完成学校 OA 系统的采购审批项目:需求采购员采购教学器材
如果金额 小于等于 5000, 由教学主任审批如果金额 小于等于 10000, 由院长审批
如果金额 小于等于 30000, 由副校长审批如果金额 超过 30000 以上,有校长审批
- 思路分析和图解(类图)
代码:
package com.atguigu.responsibilitychain;
public abstract class Approver {
Approver approver; //下一个处理者String name; // 名 字
public Approver(String name) {
// TODO Auto-generated constructor stub this.name = name;
}
//下一个处理者
public void setApprover(Approver approver) { this.approver = approver;
}
//处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
package com.atguigu.responsibilitychain;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一个请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
//创建相关的审批人
DepartmentApprover departmentApprover = new DepartmentApprover("张主任"); CollegeApprover collegeApprover = new CollegeApprover("李院长");
ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校"); SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长");
//需要将各个审批级别的下一个设置好 (处理人构成环形: ) departmentApprover.setApprover(collegeApprover); collegeApprover.setApprover(viceSchoolMasterApprover); viceSchoolMasterApprover.setApprover(schoolMasterApprover); schoolMasterApprover.setApprover(departmentApprover);
departmentApprover.processRequest(purchaseRequest); viceSchoolMasterApprover.processRequest(purchaseRequest);
}
}
package com.atguigu.responsibilitychain;
public class CollegeApprover extends Approver {
public CollegeApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
public class DepartmentApprover extends Approver {
public DepartmentApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub if(purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
//请求类
public class PurchaseRequest {
private int type = 0; //请求类型private float price = 0.0f; //请求金额
private int id = 0;
//构造器
public PurchaseRequest(int type, float price, int id) { this.type = type;
this.price = price;
this.id = id;
}
public int getType() { return type;
}
public float getPrice() { return price;
}
public int getId() { return id;
}
}
package com.atguigu.responsibilitychain;
public class SchoolMasterApprover extends Approver {
public SchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
package com.atguigu.responsibilitychain;
public class ViceSchoolMasterApprover extends Approver {
public ViceSchoolMasterApprover(String name) {
// TODO Auto-generated constructor stub super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
// TODO Auto-generated method stub
if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
}else {
approver.processRequest(purchaseRequest);
}
}
}
职责链模式在SpringMVC 框架应用的源码分析
-
SpringMVC-HandlerExecutionChain 类就使用到职责链模式
-
SpringMVC 请求流程简图
-
代码分析+Debug 源码+说明
- 对源码总结
Ø springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
Ø 在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
Ø HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
Ø HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.
职责链模式的注意事项和细节
-
将请求和处理分开,实现解耦,提高系统的灵活性
-
简化了对象,使对象不需要知道链的结构
-
性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
-
调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
-
最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat
对 Encoding 的处理、拦截器