目录
什么是设计模式?
设计模式目的
设计模式七大原则:
单一职责原则:
接口隔离原则
依赖倒转原则(Dependence Inversion Principle)
在这顺带说明聚合和组合的区别
里氏替换原则(Liskov Substitution Principle)
开闭原则(Open Closed Principle)
迪米特法则(Demeter Principle)
合成复用原则(Composite Reuse Principle)
设计模式类型
创建型模式:
单例模式
八种方式
工厂模式
抽象工厂模式
原型模式
建造者模式
结构型模式:
适配器模式
桥接模式
装饰模式
组合模式
外观模式
享元模式
代理模式:
静态代理优缺点
基本介绍
行为型模式:
模版模式
命令模式
访问者模式
迭代器模式
观察者模式
中介者模式
备忘录模式
解释器模式(Interpreter 模式)
状态模式
代码
策略模式
职责
链模式(责任链模式)
设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。
设计模式是为了让程序(软件),具有更好的
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2
有时候可以适当用方法单一来替代类的单一。
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
如下图:当类 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,
存在问题:如果接口 Interface1 对于类 A 和类 C 来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法
问题解决:
由代码可知,通过接口拆分的方法B和D实现了需要的方法,避免了实现各自不需要的方法
interface Interface1 {
void operation1();
}
interface Interface2 {
void operation2();
void operation3();
}
interface Interface3 {
void operation4();
void operation5();
}
class B implements Interface1, Interface2 {
@Override
public void operation1() {
System.out.println("B 实现了 operation1");
}
@Override
public void operation2() {
System.out.println("B 实现了 operation2");
}
@Override
public void operation3() {
System.out.println("B 实现了 operation3");
}
}
class D implements Interface1, Interface3 {
@Override
public void operation1() {
System.out.println("D 实现了 operation1");
}
@Override
public void operation4() {
System.out.println("D 实现了 operation4");
}
@Override
public void operation5() {
System.out.println("D 实现了 operation5");
}
}
/**
* A类通过接口Interface1,Interface2依赖(使用)B类,但是只会用到1,2,3方法
*/
class A {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface2 i) {
i.operation2();
}
public void depend3(Interface2 i) {
i.operation3();
}
}
/**
* C类通过接口Interface1,Interface3依赖(使用)D类,但是只会用到1,4,5方法
*/
class C {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface3 i) {
i.operation4();
}
public void depend5(Interface3 i) {
i.operation5();
}
}
设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。面向接口编程,细节依赖抽象。使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
应用实例:Person接收消息
方式1:
class Email {
public String getInfo() {
return "电子邮件信息:Hello World!";
}
}
class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
方式1分析
1.简单,比较容易想到
2.如果我们获取的对象是微信,短信等等,则新增类,同时 Peron也要增加相应的接收方法,这样做非常不利于程序扩展和优化
解决思路:
引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖,因为Email,Weixin等等属于接收的范围,他们各自实现IReceiver接口就ok,这样我们就符合依赖倒转原则。实现代码如下:通过接口传递实现的依赖倒转
interface IReceiver {
String getInfo();
}
class Email implements IReceiver {
@Override
public String getInfo() {
return "电子邮件信息:Hello World!";
}
}
class Weixin implements IReceiver {
@Override
public String getInfo() {
return "微信消息:Hello World!";
}
}
class ShortMessage implements IReceiver {
@Override
public String getInfo() {
return "短信信息:Hello World!";
}
}
class Person {
public void receive(IReceiver receiver) {
System.out.println(receiver.getInfo());
}
}
依赖倒转三种实现方式:
接口传递
//方式1:通过接口传递实现依赖
//开关的接口
interface IOpenAndClose {
void open(ITV tv);//抽象方法,接收接口
}
//ITV接口
interface ITV {
void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose {
public void open(ITV tv){
tv.play();
}
}
构造方法实现
//方式2:通过构造函数实现依赖
//开关的接口
interface IOpenAndClose {
void open();//抽象方法
}
//ITV接口
interface ITV {
public void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose {
private ITV tv; // 成员
public OpenAndClose(ITV tv){ // 构造器
this.tv = tv;
}
public void open(){
this.tv.play();
}
}
setter方式传递
//方式3,通过setter方法传递
interface IOpenAndClose{
void open();//抽象方法
void setTv(ITV tv);
}
//ITV接口
interface ITV{
void play();
}
//实现接口
class OpenAndClose implements IOpenAndClose{
private ITV tv;
public void setTv(ITV tv){
this.tv=tv;
}
public void open(){
this.tv.play();
}
}
构造方法和setter方式都属于通过聚合方式传递
现实生活中,人和人和手,脚是组合关系,因为当人死亡后人的手也就不复存在了。人和他的电脑是聚合关系。
组合实例:
public class Person {
private Hand hand;
public Person() {
hand=new Hand();
}
private void go(){
hand.hand();
}
public static void main(String[] args) {
new Person().go();//person消亡时,hand也消亡
}
}
class Hand{
public void hand(){
System.out.print("hand");
}
}
聚合实例:
public class Person2 {
private Computer c;
public Person2(){
}
public Person2(Computer c){
this.c=c;
}
public void go(){
c.computer();
}
public static void main(String[] args) {
Computer computer=new Computer();
Person2 person = new Person2(computer);
person.go();//person消亡时,不影响computer
}
}
class Computer{
public void computer(){
System.out.print("computer");
}
}
比如在A类中引用B类的一个引用b,当A类消亡时,b这个引用所指对象也
同时消亡(没有任何一个引用指向它,成了垃圾对象),这种情况叫组合,
反之b所指的对象还会有另外的引用它,这种情况叫聚合
继承性的思考和说明
基本介绍
一个程序引出的问题和思考
先看个程序,思考下问题和解决思路
public void test() {
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("---------------------");
B b = new B();
System.out.println("11-3=" + b.func1(11, 3));
System.out.println("1-8=" + b.func1(1, 8));
System.out.println("11+3+9=" + b.func2(11, 3));
}
class A {
//返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
class B extends A {
@Override
public int func1(int num1, int num2) {
return num1 + num2;
}
//增加了一个新功能:完成两个数相加,然后和9求和
public int func2(int num1, int num2) {
return func1(num1, num2) + 9;
}
}
发现问题:我们发现原来运行正常的相减功能发生了错误。原因就是类 B 无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差,特别是运行多态比较频繁的时候。
解决方法:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合、组合等关系代替
//创建一个更加基础的基类
class Base {
//将更基础的成员和方法写到Base类中
}
class A extends Base {
//返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
class B extends Base {
//如果B需要使用A类的方法,使用组合关系
private A a;
//组合实现
public B(A a) {
this.a = a;
}
public int func1(int num1, int num2) {
return num1 + num2;
}
//增加了一个新功能:完成两个数相加,然后和9求和
public int func2(int num1, int num2) {
return func1(num1, num2) + 9;
}
public int func3(int num1, int num2) {
return this.a.func1(num1, num2);
}
}
基本介绍
一个画图形的功能,
方式1:类图设计如下:
代码如下:
public class GraphicEditor {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor(new RectangleShape());
graphicEditor.drawShape();
}
private Shape shape;
public GraphicEditor(Shape shape) {
this.shape = shape;
}
public void drawShape() {
if (this.shape.m_type == 1) {
drawRectangle(this.shape);
} else if (this.shape.m_type == 2) {
drawCircle(this.shape);
} else if (this.shape.m_type == 3) {
drawTriangle(this.shape);
}
}
public void drawRectangle(Shape r) {
System.out.println("矩形");
}
public void drawCircle(Shape r) {
System.out.println("圆形");
}
public void drawTriangle(Shape r) {
System.out.println("三角形");
}
}
class Shape {
public int m_type;
public Shape() {
}
public Shape(Integer m_type) {
this.m_type = m_type;
}
}
class RectangleShape extends Shape {
RectangleShape() {
m_type = 1;
}
}
class CircleShape extends Shape {
CircleShape() {
m_type = 2;
}
}
class TriangleShape extends Shape {
TriangleShape() {
m_type = 3;
}
}
方式 1 的优缺点:
方式 1 的改进的思路:
把创建 Shape 类做成抽象类,并提供一个抽象的 draw 方法,让子类去实现即可
这样我们有新的图形种类时,只需要让新的图形类继承 Shape,并实现 draw 方法即可
使用以上方法在增加新的图形时不需要修改原有代码,满足了开闭原则
方式 2 来解决
1)方式 2 的设计方案:定义一个 Shape 抽象类
2)看代码示例
public class GraphicEditor01 {
public static void main(String[] args) {
GraphicEditor01 graphicEditor01 = new GraphicEditor01();
graphicEditor01.drawShape(new CircleShape());
}
public void drawShape(Shape s) {
s.draw();
}
}
abstract class Shape {
int m_type;
public abstract void draw();
}
class RectangleShape extends Shape {
RectangleShape() {
m_type = 1;
}
@Override
public void draw() {
System.out.println("矩形");
}
}
class CircleShape extends Shape {
CircleShape() {
m_type = 2;
}
@Override
public void draw() {
System.out.println("圆形");
}
}
class TriangleShape extends Shape {
TriangleShape() {
m_type = 3;
}
@Override
public void draw() {
System.out.println("三角形");
}
}
方式2实现了OCP原则。
基本介绍
注意事项和细节
基本介绍
原则是尽量使用合成/聚合的方式,而不是使用继承
设计原则核心思想
设计模式分为至种类型,共 23 种
介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式
1、饿汉式(静态常量)
public class Singleton {
// 1、构造器私有化
private Singleton() {
}
// 2、类的内部创建对象
private static final Singleton instance = new Singleton();
// 3、向外暴露一个静态的公共方法
public static Singleton getInstance() {
return instance;
}
}
优缺点
2、饿汉式(静态代码块)
public class Singleton {
// 1、构造器私有化
private Singleton() {
}
// 2、类的内部声明对象
private static Singleton instance;
// 3、在静态代码块中创建对象
static {
instance = new Singleton();
}
// 4、向外暴露一个静态的公共方法
public static Singleton getInstance() {
return instance;
}
}
优缺点
3、懒汉式(线程不安全) 只适合单例
class Singleton {
private static Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//即懒汉式
public static Singleton getInstance() {
//当两个线程同时进入if (instance == null),就会破坏单例
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
使用线程池模拟多线程,测试线程是否安全,拿测试结果证明,测试代码如下:
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式1 , 线程不安全~");
System.out.println("此电脑的CPU核数" + Runtime.getRuntime().availableProcessors());
int maximumPoolSize = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
maximumPoolSize,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//由运行结果可知,instance在多线程的环境下并不是单一实例
try {
//最大承载 max+queue
System.out.println("测试懒汉式线程不安全的情况");
for (int i = 1; i <= 11; i++) {
// 使用了线程池后,使用线程池来创建线程
int a = i;
threadPool.execute(() -> {
Singleton instance = Singleton.getInstance();
System.out.println("instance.hashCode=" + instance.hashCode());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
/*Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());*/
}
}
测试结果为:
优缺点
4、懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
//即懒汉式 效率低,每次线程进入getInstance方法都要进行同步
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点
getlnstance()
方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return
就行了。方法进行同步效率太低5、懒汉式(线程不安全)
public class Singleton {
// 1、构造器私有化
private Singleton() {
}
// 2、类的内部声明对象
private static Singleton instance;
// 3、向外暴露一个静态的公共方法,加入同步处理的代码,解决线程安全问题
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
测试代码:
public class SingletonTest05 {
public static void main(String[] args) {
int maximumPoolSize = Runtime.getRuntime().availableProcessors();
System.out.println("本电脑最大线程数" + maximumPoolSize);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
maximumPoolSize,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
//最大承载 max+queue
System.out.println("测试懒汉式线程安全的情况");
long startTime = System.currentTimeMillis();
for (int i = 1; i <= 11; i++) {
// 使用了线程池后,使用线程池来创建线程
threadPool.execute(() -> {
Singleton instance = Singleton.getInstance();
System.out.println("instance.hashCode=" + instance.hashCode());
});
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
测试结果:
优缺点
6、双重检查
●1)构造器私有化
●2)类的内部创建对象,同时用volatile关键字修饰修饰
●3)向外暴露一个静态的公共方法,加入同步处理的代码块,并进行双重判断,解决线程安全问题
// 懒汉式(线程安全,同步方法)
class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
//同时保证了效率, 推荐使用
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优缺点
7、静态内部类
●1)构造器私有化
●2)定义一个静态内部类,内部定义当前类的静态属性
●3)向外暴露一个静态的公共方法
public class Singleton {
// 1、构造器私有化
private Singleton() {
}
// 2、定义一个静态内部类,内部定义当前类的静态属性
private static class SingletonInstance {
private static final Singleton instance = new Singleton();
}
// 3、向外暴露一个静态的公共方法
public static Singleton getInstance() {
return SingletonInstance.instance;
}
}
优缺点
getlnstance
方法,才会装载Singletonlnstance 类,从而完成 Singleton 的实例化8、枚举
public enum Singleton {
INSTANCE;
public void sayHello() {
System.out.println("Hello World");
}
}
优缺点
注意事项和细节说明
看一个具体的需求
看一个披萨的项目:要便于披萨种类的扩展,要便于维护
传统方式
UML 类图
核心代码
public abstract class Pizza {
protected String name;
public void setName(String name) {
this.name = name;
}
public abstract void prepare();
public void bake() {
System.out.println(name + " baking...");
}
public void cut() {
System.out.println(name + " cutting...");
}
public void box() {
System.out.println(name + " boxing...");
}
}
//希腊风味披萨
public class GreekPizza extends Pizza {
@Override
public void prepare() {
setName("GreekPizza");
System.out.println(name + " preparing...");
}
}
// 奶酪披萨
public class CheesePizza extends Pizza {
@Override
public void prepare() {
setName("CheesePizza");
System.out.println(name + " preparing...");
}
}
public class OrderPizza {
public OrderPizza() {
Pizza pizza = null;
String orderType;
do {
orderType = getType();
if ("cheese".equals(orderType)) {
pizza = new CheesePizza();
} else if ("greek".equals(orderType)) {
pizza = new GreekPizza();
} else {
System.out.println("输入类型错误,程序退出");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
private String getType() {
System.out.println("请输入披萨类型:");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
return reader.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}
传统方式优缺点
// 胡椒披萨
public class PepperPizza extends Pizza {
@Override
public void prepare() {
setName("PepperPizza");
System.out.println(name + " preparing...");
}
}
public class OrderPizza {
public OrderPizza() {
// ...
else if ("pepper".equals(orderType)) {
pizza = new PepperPizza();
}
// ...
}
// ...
}
改进的思路分析
简单工厂模式
●1)简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
●2)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
●3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式
UML 类图
核心代码
public class PizzaFactory {
public Pizza createPizza(String orderType) {
Pizza pizza = null;
switch (orderType) {
case "cheese":
pizza = new CheesePizza();
break;
case "greek":
pizza = new GreekPizza();
break;
case "pepper":
pizza = new PepperPizza();
break;
default:
break;
}
return pizza;
}
}
public class OrderPizza {
private PizzaFactory pizzaFactory;
public OrderPizza(PizzaFactory pizzaFactory) {
this.pizzaFactory = pizzaFactory;
orderPizza();
}
public void orderPizza() {
Pizza pizza = null;
do {
pizza = pizzaFactory.createPizza(getType());
if (pizza == null) {
System.out.println("Failed to Order Pizza");
} else {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
} while (true);
}
// ...
}
静态工厂模式
静态工厂模式也是简单工厂模式的一种,只是将工厂方法改为静态方法
UML 类图
核心代码
public class PizzaFactory {
public static Pizza createPizza(String orderType) {
// ...
}
}
public class OrderPizza {
public OrderPizza() {
Pizza pizza;
do {
pizza = PizzaFactory.createPizza(getType());
// ...
} while (true);
}
工厂方法模式
工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
看一个新的需求
披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪 Pizza、北京的胡椒 Pizza 或者是伦敦的奶酪 Pizza、伦敦的胡椒 Pizza
思路1:使用简单工厂模式,创建不同的简单工厂类,比如 BJPizzaFactory、LDPizzaFactory 等等。从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2:使用工厂方法模式
UML 类图
核心代码
public abstract class OrderPizza {
public void orderPizza() {
Pizza pizza = null;
do {
pizza = createPizza(getType());
if (pizza == null) {
System.out.println("Failed to Order Pizza");
} else {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
} while (true);
}
public abstract Pizza createPizza(String orderType);
// ...
}
public class LDOrderPizza extends OrderPizza {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
switch (orderType) {
case "cheese":
pizza = new LDCheesePizza();
break;
case "pepper":
pizza = new LDPepperPizza();
break;
default:
break;
}
return pizza;
}
}
public class BJOrderPizza extends OrderPizza {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
switch (orderType) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "pepper":
pizza = new BJPepperPizza();
break;
default:
break;
}
return pizza;
}
}
UML 类图
核心代码
public interface AbsPizzaFactory {
Pizza createPizza(String orderType);
}
public class BJPizzaFactory implements AbsPizzaFactory {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
switch (orderType) {
case "cheese":
pizza = new BJCheesePizza();
break;
case "pepper":
pizza = new BJPepperPizza();
break;
default:
break;
}
return pizza;
}
}
public class LDPizzaFactory implements AbsPizzaFactory {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
switch (orderType) {
case "cheese":
pizza = new LDCheesePizza();
break;
case "pepper":
pizza = new LDPepperPizza();
break;
default:
break;
}
return pizza;
}
}
小结
克隆羊问题
现在有一只羊,姓名为 Tom,年龄为 1,颜色为白色,请编写程序创建和 Tom 羊属性完全相同的 10 只羊
传统方法
public class Sheep {
private String name;
private Integer age;
public Sheep(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class Client {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Sheep sheep = new Sheep("Tom", 1, "白色");
System.out.println(sheep);
}
}
}
传统方法优缺点
改进的思路分析
Java 中 Object 类是所有类的根类,Object 类提供了一个 clone 方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力 ==> 原型模式
基本介绍
对象.clone()
原型模式解决克隆羊问题
使用原型模式改进传统方式式,让程序具有更高的效率和扩展性
UML 类图
核心代码 实现Cloneable接口
public class Sheep implements Cloneable {
private String name;
private Integer age;
private String color;
public Sheep(String name, Integer age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer 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 + '\'' +
'}';
}
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return sheep;
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("Tom", 1, "白色");
for (int i = 0; i < 10; i++) {
Sheep sheep1 = (Sheep) sheep.clone();
System.out.println(sheep1);
}
}
}
JDK 源码分析
Spring 框架中,创建ApplicationContext
时,使用的getBean
方法中使用到了原型模式
浅拷贝和深拷贝
浅拷贝基本介绍
●1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
●2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
●3)前面我们克隆羊就是浅拷贝
●4)浅拷贝是使用默认的 clone 方法来实现:sheep=(Sheep)super.clone();
深拷贝基本介绍
●1)复制对象的所有基本数据类型的成员变量值
●2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
●3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝
●4)深拷贝实现方式 2:通过对象序列化实现深拷贝
public class DeepClonableTarget implements Serializable, Cloneable {
private String cloneName;
private String cloneClass;
public DeepClonableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
public String getCloneName() {
return cloneName;
}
public void setCloneName(String cloneName) {
this.cloneName = cloneName;
}
public String getCloneClass() {
return cloneClass;
}
public void setCloneClass(String cloneClass) {
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepPrototype implements Serializable, Cloneable {
private String name;
private DeepClonableTarget deepClonableTarget;
public DeepPrototype() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepClonableTarget getDeepClonableTarget() {
return deepClonableTarget;
}
public void setDeepClonableTarget(DeepClonableTarget deepClonableTarget) {
this.deepClonableTarget = deepClonableTarget;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//基本数据类型拷贝
Object object = super.clone();
//引用类型拷贝
DeepPrototype deepPrototype = (DeepPrototype) object;
deepPrototype.deepClonableTarget = (DeepClonableTarget) deepClonableTarget.clone();
return object;
}
}
public class DeepTest {
public static void main(String[] args) throws CloneNotSupportedException {
DeepPrototype prototype = new DeepPrototype();
prototype.setName("宋江");
prototype.setDeepClonableTarget(new DeepClonableTarget("及时雨", "及时雨的类"));
DeepPrototype clone1 = (DeepPrototype) prototype.clone();
DeepPrototype clone2 = (DeepPrototype) prototype.clone();
DeepPrototype clone3 = (DeepPrototype) prototype.clone();
DeepPrototype clone4 = (DeepPrototype) prototype.clone();
DeepPrototype clone5 = (DeepPrototype) prototype.clone();
System.out.println(prototype.getName() + ", " + prototype.getDeepClonableTarget().hashCode()); // 宋江, 1554874502
System.out.println(clone1.getName() + ", " + clone1.getDeepClonableTarget().hashCode()); // 宋江, 1846274136
System.out.println(clone2.getName() + ", " + clone2.getDeepClonableTarget().hashCode()); // 宋江, 1639705018
System.out.println(clone3.getName() + ", " + clone3.getDeepClonableTarget().hashCode()); // 宋江, 1627674070
System.out.println(clone4.getName() + ", " + clone4.getDeepClonableTarget().hashCode()); // 宋江, 1360875712
System.out.println(clone5.getName() + ", " + clone5.getDeepClonableTarget().hashCode()); // 宋江, 1625635731
}
}
深拷贝方式 2
public class DeepClonableTarget implements Serializable, Cloneable {
private String cloneName;
private String cloneClass;
public DeepClonableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
public String getCloneName() {
return cloneName;
}
public void setCloneName(String cloneName) {
this.cloneName = cloneName;
}
public String getCloneClass() {
return cloneClass;
}
public void setCloneClass(String cloneClass) {
this.cloneClass = cloneClass;
}
}
public class DeepPrototype implements Serializable, Cloneable {
private String name;
private DeepClonableTarget deepClonableTarget;
public DeepPrototype() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepClonableTarget getDeepClonableTarget() {
return deepClonableTarget;
}
public void setDeepClonableTarget(DeepClonableTarget deepClonableTarget) {
this.deepClonableTarget = deepClonableTarget;
}
public DeepPrototype 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);
return (DeepPrototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if (ois != null) {
ois.close();
}
if (bis != null) {
bis.close();
}
if (oos != null) {
oos.close();
}
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class DeepTest {
public static void main(String[] args) throws CloneNotSupportedException {
DeepPrototype prototype = new DeepPrototype();
prototype.setName("宋江");
prototype.setDeepClonableTarget(new DeepClonableTarget("及时雨", "及时雨的类"));
DeepPrototype clone1 = prototype.deepClone();
DeepPrototype clone2 = prototype.deepClone();
DeepPrototype clone3 = prototype.deepClone();
DeepPrototype clone4 = prototype.deepClone();
DeepPrototype clone5 = prototype.deepClone();
System.out.println(prototype.getName() + ", " + prototype.getDeepClonableTarget().hashCode()); // 宋江, 644117698
System.out.println(clone1.getName() + ", " + clone1.getDeepClonableTarget().hashCode()); // 宋江, 317574433
System.out.println(clone2.getName() + ", " + clone2.getDeepClonableTarget().hashCode()); // 宋江, 885284298
System.out.println(clone3.getName() + ", " + clone3.getDeepClonableTarget().hashCode()); // 宋江, 1389133897
System.out.println(clone4.getName() + ", " + clone4.getDeepClonableTarget().hashCode()); // 宋江, 1534030866
System.out.println(clone5.getName() + ", " + clone5.getDeepClonableTarget().hashCode()); // 宋江, 664223387
}
}
方式 1 和方式 2 对比
注意事项和细节
盖房项目需求
传统方式
问题分析
基本介绍
建造者模式的四个角色
public class House {
private String pile;
private String wall;
private String roof;
public String getPile() {
return pile;
}
public void setPile(String pile) {
this.pile = pile;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoof() {
return roof;
}
public void setRoof(String roof) {
this.roof = roof;
}
}
public abstract class HouseBuilder {
private House house = new House();
public abstract void piling();
public abstract void walling();
public abstract void capping();
public House build() {
return house;
}
}
public class NormalRoomBuilder extends HouseBuilder {
@Override
public void piling() {
System.out.println("普通房打桩...");
}
@Override
public void walling() {
System.out.println("普通房砌墙...");
}
@Override
public void capping() {
System.out.println("普通房封顶...");
}
}
public class HighRiseBuilder extends HouseBuilder {
@Override
public void piling() {
System.out.println("高楼打桩...");
}
@Override
public void walling() {
System.out.println("高楼砌墙...");
}
@Override
public void capping() {
System.out.println("高楼封顶...");
}
}
public class VillaBuilder extends HouseBuilder {
@Override
public void piling() {
System.out.println("别墅打桩...");
}
@Override
public void walling() {
System.out.println("别墅砌墙...");
}
@Override
public void capping() {
System.out.println("别墅封顶...");
}
}
public class HouseDirector {
private HouseBuilder houseBuilder;
public HouseDirector() {
}
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public House buildHouse() {
houseBuilder.piling();
houseBuilder.walling();
houseBuilder.capping();
return houseBuilder.build();
}
}
public class BuilderTest {
public static void main(String[] args) {
HouseDirector houseDirector = new HouseDirector();
House house;
houseDirector.setHouseBuilder(new NormalRoomBuilder());
house = houseDirector.buildHouse();
houseDirector.setHouseBuilder(new HighRiseBuilder());
house = houseDirector.buildHouse();
houseDirector.setHouseBuilder(new VillaBuilder());
house = houseDirector.buildHouse();
}
}
注意事项及细节
泰国旅游使用插座问题
现实生活中的适配器例子
泰国插座用的是两孔的(欧标),可以买个多功能转换插头(适配器),这样就可以使用了
基本介绍
工作原理
类适配器模式
案例
基本介绍:Adapter 类,通过继承 src 类,实现 dst 类接口,完成 src -> dst 的适配
以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src(即被适配者),我们的 dst(即目标)是 5V 直流电
UML 类图
// 被适配的类
public class Voltage220V {
public Integer output220V() {
int src = 220;
System.out.println("电压=" + src + "伏");
return src;
}
}
// 适配接口
public interface IVoltage5V {
Integer output5V();
}
// 适配器
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public Integer output5V() {
int src = output220V();
int dst = src / 44;
System.out.println("电压=" + dst + "伏");
return dst;
}
}
// 使用适配器方法
public class Phone {
public void charing(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压=5伏,正在充电~");
} else {
System.out.println("电压!=5伏,无法充电~");
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charing(new VoltageAdapter());
}
}
注意事项和细节
对象适配器模式
以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src(即被适配者),我们的 dst(即目标)是 5V 直流电,使用对象适配器模式完成
UML 类图
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public Integer output5V() {
if (voltage220V == null) {
return 0;
}
int src = voltage220V.output220V();
int dst = src / 44;
System.out.println("电压=" + dst + "伏");
return dst;
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charing(new VoltageAdapter(new Voltage220V()));
}
}
注意事项和细节
1)对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst 必须是接口
接口适配器模式
public interface Interface4 {
void operation1();
void operation2();
void operation3();
void operation4();
}
public abstract class AbsAdapter implements Interface4 {
@Override
public void operation1() {
}
@Override
public void operation2() {
}
@Override
public void operation3() {
}
@Override
public void operation4() {
}
}
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
@Override
public void operation1() {
System.out.println("调用operation1方法");
}
};
absAdapter.operation1();
}
}
SpringMVC 框架源码分析
1)SpringMVC 中的 HandlerAdapter,就使用了适配器模式
2)SpringMVC 处理请求的流程回顾
具体步骤:
在第4步,使用了适配器模式:
在Spring MVC中,DispatcherServlet作为用户,HandlerAdapter作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller作为需要适配的类。
为什么要在Spring MVC中使用适配器模式?Spring MVC中的Controller种类众多,不同类型的Controller通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet直接获取对应类型的Controller,需要的自行来判断,像下面这段代码一样:
if(mappedHandler.getHandler() instanceof MultiActionController){
((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
...
}else if(...){
...
}
这样假设如果我们增加一个HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。
因此也就引入了适配器模式,来看看它是怎么运用的:
首先定义一个适配器接口:
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception;
}
实现该接口的适配器每一个Controller都有一个适配器与之对应,这样的话,每自定义一个Controller需要定义一个实现HandlerAdapter的适配器。
适配器与Controller有对应关系,而各个适配器又都是适配器接口的实现类,因此,它们都遵循相同的适配器标准,因此用户可以按照相同的方式,通过不同的Controller去处理请求。
当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet会通过handler的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的hanle()方法来调用Controller中的用于处理请求的方法。
自己动手写 SpringMVC
public interface Controller {
}
public class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
public class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
public class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
//定义一个Adapter接口
public interface HandlerAdapter {
boolean supports(Object handler);
void handle(Object handler);
}
public class AnnotationHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
public class HttpHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
public class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
public class DispatchServlet {
public static List handlerAdapters = new ArrayList<>();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此处模拟 SpringMVC 从 request 取 handler 的对象,适配器可以获取到希望的 Controller
//HttpController controller = new HttpController();
SimpleController controller = new SimpleController();
//AnnotationController controller = new AnnotationController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
//通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler),返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
}
说明
注意事项和细节
传统方式解决手机操作问题
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图:
UML 类图
问题分析
桥接模式基本介绍
原理类图
原理类图说明
桥接模式解决手机操作问题
UML 类图
核心代码
// 行为接口——品牌接口
public interface Branch {
void open();
void call();
void close();
}
// 行为实现类——华为品牌
public class Huawei implements Branch {
@Override
public void open() {
System.out.println("华为手机开机");
}
@Override
public void call() {
System.out.println("华为手机打电话");
}
@Override
public void close() {
System.out.println("华为手机关机");
}
}
// 行为实现类——小米品牌
public class Xiaomi implements Branch {
@Override
public void open() {
System.out.println("小米手机开机");
}
@Override
public void call() {
System.out.println("小米手机打电话");
}
@Override
public void close() {
System.out.println("小米手机关机");
}
}
// 行为实现类——苹果品牌
public class iPhone implements Branch {
@Override
public void open() {
System.out.println("苹果手机开机");
}
@Override
public void call() {
System.out.println("苹果手机打电话");
}
@Override
public void close() {
System.out.println("苹果手机关机");
}
}
// 桥接类——手机抽象类
public abstract class Phone {
private Branch branch;
public Phone(Branch branch) {
this.branch = branch;
}
public void open() {
branch.open();
}
public void call() {
branch.call();
}
public void close() {
branch.close();
}
}
// 桥接子类——翻盖式手机
public class FlipPhone extends Phone {
public FlipPhone(Branch branch) {
super(branch);
System.out.println("翻盖式手机");
}
@Override
public void open() {
super.open();
}
@Override
public void call() {
super.call();
}
@Override
public void close() {
super.close();
}
}
// 桥接子类——滑盖式手机
public class SlidePhone extends Phone {
public SlidePhone(Branch branch) {
super(branch);
System.out.println("滑盖式手机");
}
@Override
public void open() {
super.open();
}
@Override
public void call() {
super.call();
}
@Override
public void close() {
super.close();
}
}
// 桥接子类——直立式手机
public class UprightPhone extends Phone {
public UprightPhone(Branch branch) {
super(branch);
System.out.println("直立式手机");
}
@Override
public void open() {
super.open();
}
@Override
public void call() {
super.call();
}
@Override
public void close() {
super.close();
}
}
注意事项和细节
桥接模式其他应用场景
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
常见的应用场景
星巴克咖啡订单项目
星巴克咖啡订单项目(咖啡馆):
方案 1-解决星巴克咖啡订单项目(较差的方案)
方案 1-解决星巴克咖啡订单问题分析
方案 2-解决星巴克咖啡订单项目(好点的方案)
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)
说明:Milk、Soy、Chocolate 可以设计为 Boolean,表示是否要添加相应的调料
方案 2-解决星巴克咖啡订单问题分析
装饰者模式
定义
1)装饰者模式:动态地将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式体现了开闭原则(OCP)
2)这里提到的动态的将新功能附加到对象和 OCP 原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会
原理
装饰者模式解决星巴克咖啡订单项目
说明
装饰者模式下的订单:2份巧克力 + 一份牛奶的 LongBlack
说明
UML类图
核心代码“
// 抽象主体
public abstract class Drink {
private String desc;
private Float price;
public String getDesc() {
return desc;
}
protected void setDesc(String desc) {
this.desc = desc;
}
public Float getPrice() {
return price;
}
protected void setPrice(Float price) {
this.price = price;
}
public abstract Float cost();
}
// 具体主体
public class Coffee extends Drink {
@Override
public Float cost() {
return super.getPrice();
}
}
public class Decaf extends Coffee {
public Decaf() {
setDesc("无因咖啡");
setPrice(20.0f);
}
}
public class Espresso extends Coffee {
public Espresso() {
setDesc("意大利浓咖");
setPrice(30.0f);
}
}
public class ShortBlack extends Coffee {
public ShortBlack() {
setDesc("短黑咖啡");
setPrice(40.0f);
}
}
public class LongBlack extends Coffee {
public LongBlack() {
setDesc("美式咖啡");
setPrice(50.0f);
}
}
//装饰者
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public Float cost() {
return super.getPrice() + drink.cost();
}
}
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
setDesc("牛奶");
setPrice(3.0f);
}
}
public class Soy extends Decorator {
public Soy(Drink drink) {
super(drink);
setDesc("豆浆");
setPrice(4.0f);
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDesc("巧克力");
setPrice(5.0f);
}
}
// 调用者
public class CoffeeBar {
public static void main(String[] args) {
Drink drink = new Espresso();
System.out.println("意大利浓咖:" + drink.cost() + "美元"); // 意大利浓咖:30.0美元
drink = new Milk(drink);
System.out.println("意大利浓咖 + 1份牛奶:" + drink.cost() + "美元"); // 意大利浓咖 + 1份牛奶:33.0美元
drink = new Chocolate(drink);
System.out.println("意大利浓咖 + 1份牛奶 + 1份巧克力:" + drink.cost() + "美元"); // 意大利浓咖...:38.0美元
drink = new Chocolate(drink);
System.out.println("意大利浓咖 + 1份牛奶 + 2份巧克力:" + drink.cost() + "美元"); // 意大利浓咖...:43.0美元
}
}
说明
学校院系展示需求
编写程序展示一个学校院系结构:
需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
传统方式解决学校院系展示(类图)
问题分析
2、组合模式基本介绍
原理类图
对原理结构图的说明一即组合模式的角色及职责
Component
:这是组合中对象声明接口。在适当情况下,实现所有类共有的接口默认行为,用于访问和管理 Component
子部件。Component
可以是抽象类或者接口Leaf
:在组合中表示叶子结点,叶子结点没有子节点Composite
:非叶子结点,用于存储子部件,在Component
接口中实现子部件的相关操作。比如增加、删除解决的问题
组合模式解决这样的问题,当我们的要处理的对象可以生成一棵树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子
组合模式解决学校院系展示
UML 类图
核心代码
// Component 抽象类
public abstract class OrganizationComponent {
private String name;
public OrganizationComponent(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void add(OrganizationComponent organizationComponent) {
throw new UnsupportedOperationException();
}
public void remove(OrganizationComponent organizationComponent) {
throw new UnsupportedOperationException();
}
public abstract void print();
}
// Composite 非叶子节点
public class University extends OrganizationComponent {
List organizationComponentList = new ArrayList<>();
public University(String name) {
super(name);
}
@Override
public void add(OrganizationComponent organizationComponent) {
organizationComponentList.add(organizationComponent);
}
@Override
public void remove(OrganizationComponent organizationComponent) {
organizationComponent.remove(organizationComponent);
}
@Override
public void print() {
for (OrganizationComponent organizationComponent : organizationComponentList) {
organizationComponent.print();
}
}
}
public class College extends OrganizationComponent {
List organizationComponentList = new ArrayList<>();
public College(String name) {
super(name);
}
@Override
public void add(OrganizationComponent organizationComponent) {
organizationComponentList.add(organizationComponent);
}
@Override
public void remove(OrganizationComponent organizationComponent) {
organizationComponent.remove(organizationComponent);
}
@Override
public void print() {
System.out.println("=============" + getName() + "=============");
for (OrganizationComponent organizationComponent : organizationComponentList) {
organizationComponent.print();
}
}
}
// Leaf 叶子结点
public class Major extends OrganizationComponent {
public Major(String name) {
super(name);
}
@Override
public void print() {
System.out.println(getName());
}
}
// 客户端
public class Client {
public static void main(String[] args) {
//大学
OrganizationComponent university = new University("清华大学");
//学院
OrganizationComponent computerCollege = new College("计算机学院");
OrganizationComponent infoEngineerCollege = new College("信息工程学院");
//专业
computerCollege.add(new Major("软件工程"));
computerCollege.add(new Major("网络工程"));
computerCollege.add(new Major("计算机科学与技术"));
infoEngineerCollege.add(new Major("通信工程"));
infoEngineerCollege.add(new Major("信息工程"));
university.add(computerCollege);
university.add(infoEngineerCollege);
university.print();
//=============计算机学院=============
//软件工程
//网络工程
//计算机科学与技术
//=============信息工程学院=============
//通信工程
//信息工程
}
}
注意事项和细节
●1)简化客户端操作:客户端只需要面对一致的对象,而不用考虑整体部分或者节点叶子的问题
●2)具有较强扩展性:当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动
●3)方便创建复杂的层次结构:客户端不用理会组合里面的组成细节,容易添加节点或者叶子,从而创建出复杂的树形结构
●4)需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式
●5)要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式
外观模式基本介绍
外观模式(Facade),也叫过程模式
外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节,这个接口使得这一子系统更加容易使用
原理类图
原理类图的说明(外观模式的角色)
传统方式解决影院管理
使用外观模式解决:
外观模式的注意事项和细节
展示网站项目需求
小型的外包项目,给客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
传统方案解决网站展现项目
传统方案解决网站展现项目-问题分析
享元模式基本介绍
享元模式的原理类图
对原理图的说明——即模式的角色和职责
内部状态和外部状态
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点。所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同。当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态
举个例子:围模理论上有 361 个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生。因为内存空间有限,一台服务器很难支持更多的玩家玩围模游戏。如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题
享元模式解决网站展现项目
原理类图
UML 类图
核心代码
/**
* 内部状态,共享角色
*/
public enum Type {
新闻,
博客,
微信公众号
}
/**
* 外部状态,非共享角色
*/
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 抽象的享元角色
*/
public abstract class Website {
public abstract void use(User user);
}
/**
* 具体的享元角色
*/
public class ConcreteWebsite extends Website {
private Type type;
public ConcreteWebsite(Type type) {
this.type = type;
}
@Override
public void use(User user) {
System.out.println("网站正在使用中:类型为" + type.name() + ",使用者为" + user.getName());
}
}
/**
* 享元工厂类
*/
public class WebsiteFactory {
private static Map pool = new HashMap<>();
public static Website getWebsiteCategory(Type type) {
if (pool.get(type) == null) {
pool.put(type, new ConcreteWebsite(type));
}
return pool.get(type);
}
public static Integer getSize() {
return pool.size();
}
}
测试代码:
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());
}
}
结果:
Integer 源码分析
首先先看一段代码测试
Integer x = Integer.valueOf(127);
Integer y = new Integer(127);
Integer z = Integer.valueOf(127);
Integer w = new Integer(127);
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false
System.out.println(x == z); // true
System.out.println(w == x); // false
System.out.println(w == y); // false
我们知道:equals比较的是对象的内容,==比较的是对象的实例
●x.equals(y)结果为true:比较的是大小,所以结果为true
●x == y、w == x、w == y结果为false:由于 y 是 new 出来的,所以结果为false
●x == z结果为true:这是为什么呢???
Integer对象的valueOf方法源码
通过IntegerCache中源码大概基本分析出
●low为-128
●high为127
所以当Integer在[-128, 127]时,会返回IntegerCache的cache[]数组内容;否则,valueOf方法相当于new Integer了
也就是说,Integer.valueOf(x)方法使用的就是享元模式
另外,我们也可以分析出:
●当数值范围在时,使用方法执行速度比更快[-128, 127]valueOfnew
享元模式的注意事项和细节
●1)在享元模式这样理解,“享”就表示共享,“元”表示对象
●2)系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
●3)用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
●4)享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
●5)享元模式提高了系统的复杂度,需要分离出内部状态和外部状态。而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方
●6)使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制
●7)享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池
代理模式的基本介绍
静态代理
基本介绍
静态代理在使里时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现租同的接口或者是继承和同父类—
应用实例
应用实例
ITeacherDao
TeacherDAO
实现接口ITeacherDAO
TeacherDAOProxy
中也实现ITeacherDAO
UML 类图
核心代码
/**
* 代理接口
*/
public interface ITeacherDao {
void teach();
}
/**
* 被代理对象
*/
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
System.out.println("老师授课中...");
}
}
/**
* 代理对象
*/
public class TeacherDaoProxy implements ITeacherDao {
private ITeacherDao iTeacherDao;
public TeacherDaoProxy(ITeacherDao iTeacherDao) {
this.iTeacherDao = iTeacherDao;
}
@Override
public void teach() {
System.out.println("准备授课...");
iTeacherDao.teach();
System.out.println("结束授课...");
}
}
调用代理
//创建被代理对象
TeacherDao teacherDao = new TeacherDao();
//创建代理对象,聚合被代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用被代理对象的方法
teacherDaoProxy.teach();
动态代理
基本介绍
JDK 中生成代理对象的 API
java.lang.reflect.Proxy
newProxyInstance
方法,但是该方法需要接收三个参数,完整的写法是:static Object newProxylnstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
UML 类图
核心代码
// ITeacherDao与TeacherDao同上
/**
* 代理工厂
*/
public class TeacherFactory {
/**
* 目标对象
*/
private Object target;
public TeacherFactory(Object target) {
this.target = target;
}
public Object newProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理授课开始...");
Object returnVal = method.invoke(target, args);
System.out.println("JDK代理授课结束...");
return returnVal;
}
});
}
}
其中几个参数
ClassLoader loader
:指定当前目标对象使用的类加载器,获取加载器的方法固定Class>[] interfaces
:目标对象实现的接口类型,使用泛型方法确认类型InvocationHandler h
:事情处理,执行目标对象的方法时触发事情处理器方法,把当前执行的目标对象方法作为参数传入Cglib 代理
实现步骤
final
,否则报错java.lang.IllegalArgumentException
final
/static
,那么就不会被拦截,即不会执行目标对象额外的业务方法应用实例
UML 类图
核心代码
/**
* 被代理对象
*/
public class TeacherDao {
public String teach() {
System.out.println("老师授课中...");
return "Good";
}
}
/**
* 代理工厂类
*/
public class ProxyFactory implements MethodInterceptor {
/**
* 目标对象
*/
private Object target;
/**
* 构造函数
*
* @param target
*/
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 返回代理对象
*
* @return
*/
public Object getProxyInstance() {
// 1、创建工具类
Enhancer enhancer = new Enhancer();
// 2、设置父类
enhancer.setSuperclass(target.getClass());
// 3、设置回调函数
enhancer.setCallback(this);
// 4、创建子类对象,即代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib代理开始...");
Object retVal = method.invoke(target, args);
System.out.println("cglib代理结束...");
return retVal;
}
}
调用代理
//创建目标对象
TeacherDao teacherDao = new TeacherDao();
//通过代理工厂创建代理对象
TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
//通过代理对象调用目标对象方法
String retVal = proxyInstance.teach();
System.out.println("retVal=" + retVal);
代理模式的变体
几种常见的代理模式介绍一几种变体
豆浆制作问题
编写制作豆浆的程序,说明如下:
说明:因为模板方法模式比较简单,很容易就想到这个方案,因此就直接使用,不再使用传统的方案来引出模板方法模式
基本介绍
原理类图
对原理类图的说明——即模板方法模式的角色和职责
AbstractClass
抽象类中实现了模板方法,定义了算法的骨架,具体子类需要去实现其抽象方法或重写其中方法ConcreteClass
实现了抽象方法,已完成算法中特定子类的步骤模板模式解决豆浆制作问题
UML 类图
核心代码
/**
* 抽象方法
*/
public abstract class SoyaMilk {
/**
* 模板方法,定义为final禁止覆写
*/
public final void make() {
System.out.println(">>>>>>豆浆制作开始<<<<<<");
useSoyBean();
addIngredients();
soak();
mash();
System.out.println(">>>>>>豆浆制作结束<<<<<<");
}
protected void useSoyBean() {
System.out.println("Step1. 选用上好的黄豆.");
}
protected abstract void addIngredients();
protected void soak() {
System.out.println("Step3. 对黄豆和配料进行水洗浸泡.");
}
protected void mash() {
System.out.println("Step4. 将充分浸泡过的黄豆和配料放入豆浆机中,开始打豆浆.");
}
}
/**
* 花生豆浆
*/
public class PeanutSoyaMilk extends SoyaMilk {
public PeanutSoyaMilk() {
System.out.println("============花生豆浆============");
}
@Override
protected void addIngredients() {
System.out.println("Step2. 加入上好的花生.");
}
}
/**
* 红豆豆浆
*/
public class RedBeanSoyaMilk extends SoyaMilk {
public RedBeanSoyaMilk() {
System.out.println("============红豆豆浆============");
}
@Override
protected void addIngredients() {
System.out.println("Step2. 加入上好的红豆.");
}
}
/**
* 芝麻豆浆
*/
public class SesameSoyaMilk extends SoyaMilk {
public SesameSoyaMilk() {
System.out.println("============芝麻豆浆============");
}
@Override
protected void addIngredients() {
System.out.println("Step2. 加入上好的芝麻.");
}
}
调用模板方法:
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
SoyaMilk sesameSoyaMilk = new SesameSoyaMilk();
sesameSoyaMilk.make();
/*
============花生豆浆============
>>>>>>豆浆制作开始<<<<<<
Step1. 选用上好的黄豆.
Step2. 加入上好的花生.
Step3. 对黄豆和配料进行水洗浸泡.
Step4. 将充分浸泡过的黄豆和配料放入豆浆机中,开始打豆浆.
>>>>>>豆浆制作结束<<<<<<
============红豆豆浆============
>>>>>>豆浆制作开始<<<<<<
Step1. 选用上好的黄豆.
Step2. 加入上好的红豆.
Step3. 对黄豆和配料进行水洗浸泡.
Step4. 将充分浸泡过的黄豆和配料放入豆浆机中,开始打豆浆.
>>>>>>豆浆制作结束<<<<<<
============芝麻豆浆============
>>>>>>豆浆制作开始<<<<<<
Step1. 选用上好的黄豆.
Step2. 加入上好的芝麻.
Step3. 对黄豆和配料进行水洗浸泡.
Step4. 将充分浸泡过的黄豆和配料放入豆浆机中,开始打豆浆.
>>>>>>豆浆制作结束<<<<<<
*/
注意事项和细节
final
关键字,防止子类重写模板方法智能生活项目需求
●1)我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,只要在手机上安装 APP 就可以控制这些家电的工作
●2)这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个 APP 分别控制,我们希望只要一个 APP 就可以控制全部智能家电
●3)要实现一个 APP 控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给 APP 调用,这时就可以考虑使用命令模式
●4)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来
●5)在我们的例子中,动作的请求者是手机 APP,动作的执行者是每个厂商的一个家电产品
基本介绍
●1)命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可。此时可以使用命令模式来进行设计
●2)命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦
●3)在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作
●4)通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:
○将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)
○Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对
原理类图
命令模式的角色及职责
●Invoker调用者角色,只需要发布命令就可以控制接收者的行为
●Receiver接收者角色,知道如何实施或执行请求的相关操作
●Command命令角色,需要执行的所有命令都定义在这里,可以是接口或抽象类
●ConcreteCommand具体的命令角色,将一个接收者和一个动作绑定,调用接收者相应的操作,实现execute
核心代码
/**
* 命令角色
*/
public interface Command {
void execute();
void undo();
}
/**
* 空命令,什么也不干
*/
public class NonCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
/**
* 调用者
*/
public class RemoteController {
private Command[] onCommands;
private Command[] offCommands;
private Command restoreCommand;
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NonCommand();
offCommands[i] = new NonCommand();
}
}
public void setCommands(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void onBtnCommand(int no) {
restoreCommand = onCommands[no];
restoreCommand.execute();
}
public void offBtnCommand(int no) {
restoreCommand = offCommands[no];
restoreCommand.execute();
}
public void undoBtnCommand() {
restoreCommand.undo();
}
}
/**
* 接收者
*/
public class LightReceiver {
public void on() {
System.out.println("电灯打开了...");
}
public void off() {
System.out.println("电灯关闭了...");
}
}
public class TVReceiver {
public void on() {
System.out.println("电视打开了...");
}
public void off() {
System.out.println("电视关闭了...");
}
}
/**
* 具体的命令角色
*/
public class LightOnCommand implements Command{
private LightReceiver light;
public LightOnCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
public class LightOffCommand implements Command {
private LightReceiver light;
public LightOffCommand(LightReceiver light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
public class TVOnCommand implements Command {
private TVReceiver tv;
public TVOnCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
public class TVOffCommand implements Command {
private TVReceiver tv;
public TVOffCommand(TVReceiver tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
调用命令
// 初始化遥控器
RemoteController remoteController = new RemoteController();
// 操作电灯
int no = 0;
LightReceiver light = new LightReceiver();
remoteController.setCommands(no, new LightOnCommand(light), new LightOffCommand(light));
remoteController.onBtnCommand(no);
remoteController.offBtnCommand(no);
remoteController.undoBtnCommand();
// 操作电视
no = 1;
TVReceiver tv = new TVReceiver();
remoteController.setCommands(no, new TVOnCommand(tv), new TVOffCommand(tv));
remoteController.onBtnCommand(no);
remoteController.offBtnCommand(no);
remoteController.undoBtnCommand();
// 电灯打开了...
// 电灯关闭了...
// 电灯打开了...
// 电视打开了...
// 电视关闭了...
// 电视打开了...
命令模式的注意事项和细节
execute()
方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用基本介绍
完成测评系统需求
将人、观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如成功、失败等)
传统方案
传统方式的问题分析
访问者模式实现测评系统
uml类图
核心代码
/**
* 抽象访问者
*/
public abstract class Action {
public abstract void getManResult(Man man);
public abstract void getWomanResult(Woman woman);
}
/**
* 具体访问者
*/
public class Success extends Action {
@Override
public void getManResult(Man man) {
System.out.println("男生给了通过");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女生给了通过");
}
}
public class Fail extends Action {
@Override
public void getManResult(Man man) {
System.out.println("男生给了不通过");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女生给了不通过");
}
}
public class Wait extends Action {
@Override
public void getManResult(Man man) {
System.out.println("男生给了待定");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女生给了待定");
}
}
/**
* 抽象元素
*/
public abstract class Person {
public abstract void accept(Action action);
}
/**
* 具体元素
*/
public class Man extends Person {
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
public class Woman extends Person {
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}
/**
* 对象结构
*/
public class ObjectStructure {
private List personList = new ArrayList<>();
public void attach(Person p) {
personList.add(p);
}
public void detach(Person p) {
personList.remove(p);
}
public void display(Action action) {
for (Person person : personList) {
person.accept(action);
}
}
}
测试代码
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.attach(new Man());
objectStructure.attach(new Woman());
objectStructure.display(new Success());
System.out.println("============");
objectStructure.display(new Fail());
System.out.println("============");
objectStructure.display(new Wait());
//男生给了通过
//女生给了通过
//============
//男生给了不通过
//女生给了不通过
//============
//男生给了待定
//女生给了待定
双分派
该例中我们使用到了双分派
Woman
中Woman
类调用作为参数的具体方法getWomanResult
,同时将自己this
作为参数传入所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行
双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型
以上述实例为例,假设我们要添加一个Wait
的状态类,考察Man
类和Woman
类的反应
由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码
访问者模式的注意事项和细节
优点
●1)访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
●2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
●1)具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
●2)违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
●3)因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
学校院系结构展示需求
编写程序展示一个学校院系结构,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
传统方案分析
基本介绍
原理类图
迭代器模式的角色及职责
Iterator
迭代器接口:系统提供,含有hasNext
、next
、remove
ConcreteIterator
具体的迭代器:管理相关的迭代Aggregate
聚合接口:将客户端和具体的聚合解耦ConcreteAggregate
具体的聚合类:提供一个方法,返回可以正确遍历集合的迭代器Client
客户端:通过Iterator
迭代器接口和Aggregate
聚合接口依赖其具体的迭代器和聚合子类迭代器完成学校院系结构展示需求
UML 类图
核心代码
具体的迭代器
/**
* 计算机学院迭代器类
*/
public class ComputerCollegeIterator implements Iterator {
private Department[] departments;
private Integer position = -1;
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return position + 1 < departments.length && departments[position + 1] != null;
}
@Override
public Object next() {
return departments[++position];
}
}
/**
* 信息学院迭代器类
*/
public class InfoCollegeIterator implements Iterator {
private List departments;
private Integer position = -1;
public InfoCollegeIterator(List departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return position + 1 < departments.size();
}
@Override
public Object next() {
return departments.get(++position);
}
}
聚合接口
public interface College {
String getName();
Iterator createIterator();
}
具体的聚合类
/**
* 计算机学院
*/
public class ComputerCollege implements College {
private Department[] departments;
private Integer position = 0;
public ComputerCollege() {
departments = new Department[5];
departments[position++] = new Department("Java专业");
departments[position++] = new Department("PHP专业");
departments[position++] = new Department("Python专业");
}
@Override
public String getName() {
return "计算机学院";
}
@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}
/**
* 信息学院
*/
public class InfoCollege implements College {
private List departments;
public InfoCollege() {
departments = new ArrayList<>();
departments.add(new Department("信息安全专业"));
departments.add(new Department("网络安全专业"));
departments.add(new Department("服务器安全专业"));
}
@Override
public String getName() {
return "信息学院";
}
@Override
public Iterator createIterator() {
return new InfoCollegeIterator(departments);
}
}
输出类
public class OutPutImpl {
public OutPutImpl() {
}
public void printCollege(List collegeList) {
Iterator iterator = collegeList.iterator();
while (iterator.hasNext()) {
College college = iterator.next();
System.out.println("============" + college.getName() + "============");
printDepartment(college);
}
}
private void printDepartment(College college) {
Iterator iterator = college.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getName());
}
}
}
调用测试
List collegeList = new ArrayList<>();
collegeList.add(new ComputerCollege());
collegeList.add(new InfoCollege());
new OutPutImpl().printCollege(collegeList);
打印结果
ArrayList 集合源码分析
UML 类图
角色分析说明
●Iterator迭代器接口:由系统提供,定义了hasNext()和next()等方法
●Itr具体的迭代器实现类:作为ArrayList的内部类存在,实现了Iterator接口的hasNext()和next()等方法
●List聚合接口:定义了iterator()方法,返回一个迭代器接口对象
●ArrayList具体的聚合类:实现了iterator()方法
迭代器模式提供了一个不同集合类型(如ArrayList、LinkedList等)的统一遍历解决方案
迭代器模式的注意事项和细节
优点
●1)提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了
●2)隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成
●3)提供了一种设计思想,就是一个类应该只有一个引起变化的原因(单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器
●4)当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式
缺点
●每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类
天气预报需求
具体要求如下:
天气预报需求方案之普通方案
WeatherData类
通过对气象站项目的分析,我们可以初步设计出一个WeatherData
类
getXxx
方法,可以让第三方接入,并得到相关信息dataChange()
去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送CurrentConditions
(当前的天气情况)可以理解成是我们气象局的网站
核心代码
气象网站类
/**
* 当前的天气情况:可以理解成是气象局的网站
*/
public class CurrentConditions {
private Float temperature;
private Float pressure;
private Float humidity;
/**
* 更新天气情况,通过推送的方式,由 WeatherData 调用
*
* @param temperature
* @param pressure
* @param humidity
*/
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("*** 当前温度:" + this.temperature + " ℃ ***");
System.out.println("*** 当前气压:" + this.pressure + " kPa ***");
System.out.println("*** 当前湿度:" + this.humidity + " %RH ***");
}
}
气象数据类
/**
* 核心类
* 1、包含最新的天气信息情况
* 2、含有 CurrentConditions 对象
* 3、当数据更新时,主动调用 CurrentConditions 的 update() 方法
*/
public class WeatherData {
private Float temperature;
private Float pressure;
private Float humidity;
private CurrentConditions conditions;
/**
* 传入 CurrentConditions 对象
*
* @param conditions
*/
public WeatherData(CurrentConditions conditions) {
this.conditions = conditions;
}
public Float getTemperature() {
return temperature;
}
public Float getPressure() {
return pressure;
}
public Float getHumidity() {
return humidity;
}
/**
* 推送天气数据到网站
*/
public void dataChange() {
conditions.update(getTemperature(), getPressure(), getHumidity());
}
/**
* 当天气数据发生变化时进行更新
*
* @param temperature
* @param pressure
* @param humidity
*/
public void setData(Float temperature, Float pressure, Float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
}
客户端调用
// 创建气象网站对象
CurrentConditions currentConditions = new CurrentConditions();
// 创建气象数据对象,并传入气象网站对象
WeatherData weatherData = new WeatherData(currentConditions);
// 天气发生变化时,更新最新的气象数据
weatherData.setData(10f, 150f, 40f);
//weatherData.setData(15f, 130f, 60f);
//weatherData.setData(13f, 160f, 20f);
测试结果
============最新天气============
*** 当前温度:10.0 ℃ ***
*** 当前气压:150.0 kPa ***
*** 当前湿度:40.0 %RH ***
============最新天气============
*** 当前温度:15.0 ℃ ***
*** 当前气压:130.0 kPa ***
*** 当前湿度:60.0 %RH ***
============最新天气============
*** 当前温度:13.0 ℃ ***
*** 当前气压:160.0 kPa ***
*** 当前湿度:20.0 %RH ***
问题分析
OCP
原则 => 观察者模式在WeatherData
中增加第三方时,都需要创建对应的第三方公台板对象并加入到dataChange()
方法中,既不是动态加入,也不利于维护
观察者模式原理
观察者模式类似订牛奶业务
Subject
Observer
Subject
:登记注册、移除和通知
registerObserver()
:注册removeObserver()
:移除notifyObservers()
:通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定Observer
:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject
,依赖的对象为Observer
,Subject
通知Observer
变化,比如这里的奶站是Subject
,是1的一方。用户是Observer
,是多的一方
天气预报需求方案之观察者模式
UML 类图
核心代码
观察者对象Observer
/**
* 观察者接口
*/
public interface Observer {
void update(Float temperature, Float pressure, Float humidity);
}
/**
* 观察者实现
*/
public class CurrentConditions implements Observer {
private Float temperature;
private Float pressure;
private Float humidity;
@Override
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("*** 当前温度:" + this.temperature + " ℃ ***");
System.out.println("*** 当前气压:" + this.pressure + " kPa ***");
System.out.println("*** 当前湿度:" + this.humidity + " %RH ***");
}
}
主体对象Subject
/**
* 主体对象接口
*/
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
/**
* 主体对象实现
*/
public class WeatherData implements Subject {
private Float temperature;
private Float pressure;
private Float humidity;
private List observerList;
public WeatherData() {
observerList = new ArrayList<>();
}
public Float getTemperature() {
return temperature;
}
public Float getPressure() {
return pressure;
}
public Float getHumidity() {
return humidity;
}
/**
* 推送天气数据到网站
*/
public void dataChange() {
notifyObservers();
}
/**
* 当天气数据发生变化时进行更新
*
* @param temperature
* @param pressure
* @param humidity
*/
public void setData(Float temperature, Float pressure, Float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
@Override
public void removeObserver(Observer o) {
if(o!= null && observerList.contains(o)) {
observerList.remove(o);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observerList) {
observer.update(temperature, pressure, humidity);
}
}
}
观察者对象Observer
public interface Observer {
void update(Float temperature, Float pressure, Float humidity);
}
调用测试
// 创建气象网站对象
CurrentConditions currentConditions = new CurrentConditions();
// 创建气象数据对象
WeatherData weatherData = new WeatherData();
// 注册气象网站对象
weatherData.registerObserver(currentConditions);
// 天气发生变化时,更新最新的气象数据
weatherData.setData(10f, 150f, 40f);
//============最新天气============
//*** 当前温度:10.0 ℃ ***
//*** 当前气压:150.0 kPa ***
//*** 当前湿度:40.0 %RH ***
观察者模式的好处
Observer
,包括注册、移除和通知WeatherData
不会修改代码,遵守了ocp
原则例如,我们新增SinaWebSite
和BaiDuWebSite
两个三方网站,接口气象局。此时三方只需实现相应接口即可,WeatherData
不需要有任何的改变
/**
* 新增的三方观察者对象——新浪网
*/
public class SinaWebSite implements Observer {
private Float temperature;
private Float pressure;
private Float humidity;
/**
* 更新天气情况,通过推送的方式,由 WeatherData 调用
*
* @param temperature
* @param pressure
* @param humidity
*/
@Override
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("*** 新浪网-当前温度:" + this.temperature + " ℃ ***");
System.out.println("*** 新浪网-当前气压:" + this.pressure + " kPa ***");
System.out.println("*** 新浪网-当前湿度:" + this.humidity + " %RH ***");
}
}
/**
* 新增的三方观察者对象——百度网
*/
public class BaiDuWebSite implements Observer {
private Float temperature;
private Float pressure;
private Float humidity;
/**
* 更新天气情况,通过推送的方式,由 WeatherData 调用
*
* @param temperature
* @param pressure
* @param humidity
*/
@Override
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("*** 百度网-当前温度:" + this.temperature + " ℃ ***");
System.out.println("*** 百度网-当前气压:" + this.pressure + " kPa ***");
System.out.println("*** 百度网-当前湿度:" + this.humidity + " %RH ***");
}
}
调用测试
// 新增三方气象网站,只需注册即可
weatherData.registerObserver(new SinaWebSite());
weatherData.registerObserver(new BaiDuWebSite());
// 天气发生变化时,更新最新的气象数据
weatherData.setData(15f, 120f, 80f);
//============最新天气============
//*** 当前温度:15.0 ℃ ***
//*** 当前气压:120.0 kPa ***
//*** 当前湿度:80.0 %RH ***
//============新浪网-最新天气============
//*** 新浪网-当前温度:15.0 ℃ ***
//*** 新浪网-当前气压:120.0 kPa ***
//*** 新浪网-当前湿度:80.0 %RH ***
//============百度网-最新天气============
//*** 百度网-当前温度:15.0 ℃ ***
//*** 百度网-当前气压:120.0 kPa ***
//*** 百度网-当前湿度:80.0 %RH ***
当三方网站不再需要时,只要做相应的移除即可
// 移除气象网站
weatherData.removeObserver(currentConditions);
weatherData.setData(20f, 160f, 30f);
//============新浪网-最新天气============
//*** 新浪网-当前温度:20.0 ℃ ***
//*** 新浪网-当前气压:160.0 kPa ***
//*** 新浪网-当前湿度:30.0 %RH ***
//============百度网-最新天气============
//*** 百度网-当前温度:20.0 ℃ ***
//*** 百度网-当前气压:160.0 kPa ***
//*** 百度网-当前湿度:30.0 %RH ***
Mediator Pattern
),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互Controller
控制器)是M(Model
模型)和V(View
视图)的中介者,在前后端交互时起到了中间人的作用原理类图
中介者模式角色及职责
Mediator
【抽象中介者】:定义了同事对象到中介者对象的接口ConcreteMediator
【具体的中介者对象】:实现抽象中介者方法,需要知道所有具体的同事类,即以一个集合来管理HashMap
,并接受某个同事对象消息,完成相应的任务Colleague
【抽象同事类】ConcreteColleague
【具体的同事类】:会有很多,只知道自己的行为,而不了解其他同事类的行为(方法),但他们都依赖中介者对象中介者模式解决智能家庭管理问题
UML 类图
智能家庭管理操作流程
ConcreMediator
对象Alarm
、CoffeeMachine
、TV
...colleagueMap
sendMessage
,最终会去调用ConcreteMediator
的getMessage
方法getMessage
会根据接收到的同事对象发出的消息,来协调调用其它的同事对象,完成任务getMessage
是核心方法,完成相应任务核心代码
抽象中介者
public abstract class Mediator {
public abstract void registerColleague(Colleague colleague);
public abstract void getMsg(Integer state, String name);
public abstract void sendMsg();
}
抽象同事类
public abstract class Colleague {
private Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public Mediator getMediator() {
return this.mediator;
}
public void sendMsg(Integer state) {
this.getMediator().getMsg(state, this.getClass().getSimpleName());
}
}
具体同事类
/**
* 闹钟
*/
public class Alarm extends Colleague {
public Alarm(Mediator mediator) {
super(mediator);
this.getMediator().registerColleague(this);
}
/**
* 闹铃响起
*/
public void openAlarm() {
System.out.println(">>>闹铃响起");
try {
//模拟闹铃耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sendMsg(1);
}
/**
* 闹铃关闭
*/
public void closeAlarm() {
System.out.println(">>>闹铃关闭");
sendMsg(0);
}
}
/**
* 咖啡机
*/
public class CoffeeMachine extends Colleague {
public CoffeeMachine(Mediator mediator) {
super(mediator);
this.getMediator().registerColleague(this);
}
/**
* 煮咖啡
*/
public void makeCoffee() {
System.out.println(">>>煮咖啡中...");
sendMsg(0);
try {
//模拟煮咖啡耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 煮咖啡完毕
*/
public void completeCoffee() {
System.out.println(">>>咖啡已煮好");
sendMsg(1);
}
}
/**
* 窗帘
*/
public class Curtain extends Colleague {
public Curtain(Mediator mediator) {
super(mediator);
this.getMediator().registerColleague(this);
}
/**
* 拉起窗帘
*/
public void upCurtain() {
System.out.println(">>>拉起窗帘...");
sendMsg(1);
try {
//模拟拉起窗帘耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 拉下窗帘
*/
public void downCurtain() {
System.out.println(">>>拉下窗帘...");
sendMsg(0);
try {
//模拟拉下窗帘耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 电视机
*/
public class TV extends Colleague {
public TV(Mediator mediator) {
super(mediator);
this.getMediator().registerColleague(this);
}
/**
* 打开电视
*/
public void openTV() {
System.out.println(">>>打开电视...");
sendMsg(1);
}
/**
* 关闭电视
*/
public void closeTV() {
System.out.println(">>>关闭电视...");
sendMsg(0);
}
/**
* 切换频道
*/
public void switchChannel(Integer state) {
System.out.println(">>>切换频道:" + state);
sendMsg(state);
try {
//模拟看电视耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试代码
//创建中介者
Mediator mediator = new ConcreteMediator();
//创建各个同事类,并加入Mediator中介者的Map对象中
Alarm alarm = new Alarm(mediator);
CoffeeMachine coffeeMachine = new CoffeeMachine(mediator);
Curtain curtain = new Curtain(mediator);
TV tv = new TV(mediator);
//闹钟响起
alarm.openAlarm();
coffeeMachine.completeCoffee();
tv.closeTV();
//>>>闹铃响起
//>>>闹铃关闭
//>>>煮咖啡中...
//>>>咖啡已煮好
//>>>拉下窗帘...
//>>>打开电视...
//>>>切换频道:101
//>>>关闭电视...
//>>>拉起窗帘...
中介者模式的注意事项和细节
优点
缺点
备忘录模式基本介绍
●1)备忘录模式(Memento Pattern):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
●2)可以这样理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
●3)备忘录模式属于行为型模式
原理类图
示例代码
/**
* 源对象
*/
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento createMementor() {
return new Memento(state);
}
public void revertStateFromMementor(Memento memento) {
this.state = memento.getState();
}
}
/**
* 备忘录对象
*/
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
/**
* 守护者对象
*/
public class Caretaker {
private List mementoList = new ArrayList<>();
public void addMemento(Memento memento) {
mementoList.add(memento);
}
public Memento getMemento(Integer index) {
return mementoList.get(index);
}
}
测试代码
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("当前状态:" + " 状态#1 血量 100 ");
caretaker.addMemento(originator.createMementor());
System.out.println(originator.getState());
originator.setState("当前状态:" + " 状态#2 血量 80 ");
caretaker.addMemento(originator.createMementor());
System.out.println(originator.getState());
originator.setState("当前状态:" + " 状态#3 血量 60 ");
caretaker.addMemento(originator.createMementor());
System.out.println(originator.getState());
// 恢复到状态1
originator.revertStateFromMementor(caretaker.getMemento(0));
System.out.println("恢复状态:" + originator.getState());
//当前状态: 状态#1 血量 100
//当前状态: 状态#2 血量 80
//当前状态: 状态#3 血量 60
//恢复状态:当前状态: 状态#1 血量 100
备忘录模式中的角色和职责
Originator
源对象:需要保存状态的对象Memento
备忘录对象:负责保存Originator
内部状态Caretaker
守护者对象:负责存放多个Memento
对象,使用集合管理,提高效率Originator
对象的不同内部状态,也可以使用Map>
备忘录模式的注意事项和细节
优点
缺点
其他
Windows
里的ctrl+z
IE
中的后退APP 抽奖活动问题
编写程序完成 APP 抽奖活动具体要求如下:
状态模式基本介绍
原理类图
角色与职责
Context
环境角色:维护一个State
实例,这个实例定义了当前状态State
抽象状态角色:定义一个接口,封装与Context
的一个特点接口相关行为ConcreteState
具体状态角色:实现一个与Context
的一个状态相关行为状态模式解决 APP 抽奖问题
UML 类图
核心代码
抽象状态角色
/**
* 抽象状态角色
*/
public interface State {
Boolean reduceMoney();
Boolean raffle();
Boolean dispensePrize();
}
不能抽奖状态类
/**
* 不能抽奖状态类
*/
public class NoRaffleState implements State {
private RaffleActivity raffleActivity;
// 模拟数据库积分值
private int integral = 100;
public NoRaffleState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
@Override
public Boolean reduceMoney() {
if (integral < 50) {
System.out.println("您的积分余额不足~");
return false;
}
integral -= 50;
raffleActivity.setCanRaffleState();
System.out.println("已扣除50积分,可以进行抽奖啦~");
return true;
}
@Override
public Boolean raffle() {
System.out.println("当前无法进行抽奖~");
return false;
}
@Override
public Boolean dispensePrize() {
System.out.println("当前无法领取奖品~");
return false;
}
}
可以抽奖状态类
/**
* 可以抽奖状态类
*/
public class CanRaffleState implements State {
private RaffleActivity raffleActivity;
public CanRaffleState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
@Override
public Boolean reduceMoney() {
System.out.println("已扣除50积分,可以进行抽奖啦~");
return false;
}
@Override
public Boolean raffle() {
if (new Random().nextInt(10) == 0) {
raffleActivity.setDispenseState();
System.out.println("恭喜您,中奖了~");
return true;
}
raffleActivity.setNoRaffleState();
System.out.println("很遗憾,您没有中奖~");
return false;
}
@Override
public Boolean dispensePrize() {
System.out.println("尚未进行抽奖,无法领取奖品!");
return false;
}
}
发放奖品状态类
/**
* 发放奖品状态类
*/
public class DispenseState implements State {
private RaffleActivity raffleActivity;
public DispenseState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
@Override
public Boolean reduceMoney() {
System.out.println("已经进行过抽奖啦!");
return false;
}
@Override
public Boolean raffle() {
System.out.println("已经进行过抽奖啦!");
return false;
}
@Override
public Boolean dispensePrize() {
if (raffleActivity.getCount() <= 0) {
raffleActivity.setDispenseOutState();
System.out.println("今日奖品已领完,明天再来吧~");
return false;
}
raffleActivity.setNoRaffleState();
System.out.println("奖品领取成功~");
return true;
}
}
奖品领完状态类
/**
* 奖品领完状态类
*/
public class DispenseOutState implements State {
private RaffleActivity raffleActivity;
public DispenseOutState(RaffleActivity raffleActivity) {
this.raffleActivity = raffleActivity;
}
@Override
public Boolean reduceMoney() {
System.out.println("今日奖品已领完,明天再来吧~");
return false;
}
@Override
public Boolean raffle() {
System.out.println("今日奖品已领完,明天再来吧~");
return false;
}
@Override
public Boolean dispensePrize() {
System.out.println("今日奖品已领完,明天再来吧~");
return false;
}
}
测试代码
RaffleActivity raffleActivity = new RaffleActivity(2);
// 第一次抽奖
System.out.println("======第一次抽奖======");
raffleActivity.raffle();
// 第二次抽奖
System.out.println("======第二次抽奖======");
raffleActivity.raffle();
// 第三次抽奖
System.out.println("======第三次抽奖======");
raffleActivity.raffle();
//======第一次抽奖======
//已扣除50积分,可以进行抽奖啦~
//很遗憾,您没有中奖~
//======第二次抽奖======
//已扣除50积分,可以进行抽奖啦~
//恭喜您,中奖了~
//奖品领取成功~
//======第三次抽奖======
//您的积分余额不足~
借贷平台—流程审批
状态模式本质上是一种基于状态和事件的状态机,下面是订单流程的状态图
通过状态图,我们再设计一张横纵坐标关系表来比较,如下图
状态 \ 事件 |
电审(1) |
电审失败(2) |
定价发布(3) |
接单(4) |
接单失败(5) |
付款(6) |
支付失效(7) |
反馈(8) |
订单生成(1) |
已审核(2) |
已完结(6) |
||||||
已审核(2) |
已发布(3) |
|||||||
已发布(3) |
待付款(4) |
已完结(6) |
||||||
待付款(4) |
已付款(5) |
已完结(6) |
||||||
已付款(5) |
已完结(6) |
|||||||
已完结(6) |
UML 类图
状态接口
/**
* 状态接口
*/
public interface State {
/**
* 电审
*
* @param context
*/
void electronicAudit(Context context);
/**
* 电审失败
*
* @param context
*/
void electronicAuditFail(Context context);
/**
* 定价发布
*
* @param context
*/
void releasePricing(Context context);
/**
* 接单
*
* @param context
*/
void acceptOrder(Context context);
/**
* 接单失败
*
* @param context
*/
void acceptOrderFail(Context context);
/**
* 付款
*
* @param context
*/
void payMoney(Context context);
/**
* 反馈【下款 或 拒贷】
*
* @param context
*/
void feedback(Context context);
String getCurrentState();
}
抽象状态类
/**
* 抽象状态类,默认实现
*/
public abstract class AbstractState implements State {
private static final RuntimeException EXCEPTION = new RuntimeException("操作流程有误");
@Override
public void electronicAudit(Context context) {
throw EXCEPTION;
}
@Override
public void electronicAuditFail(Context context) {
throw EXCEPTION;
}
@Override
public void releasePricing(Context context) {
throw EXCEPTION;
}
@Override
public void acceptOrder(Context context) {
throw EXCEPTION;
}
@Override
public void acceptOrderFail(Context context) {
throw EXCEPTION;
}
@Override
public void payMoney(Context context) {
throw EXCEPTION;
}
@Override
public void feedback(Context context) {
throw EXCEPTION;
}
}
具体状态类
/**
* 订单生成状态类
*/
public class GeneratedState extends AbstractState {
@Override
public void electronicAudit(Context context) {
context.setState(new AuditedState());
}
@Override
public void electronicAuditFail(Context context) {
context.setState(new FinishedState());
}
@Override
public String getCurrentState() {
return StateEnum.GENERATED.name();
}
}
/**
* 已审核状态类
*/
public class AuditedState extends AbstractState {
@Override
public void releasePricing(Context context) {
context.setState(new PublishedState());
}
@Override
public String getCurrentState() {
return StateEnum.AUDITED.name();
}
}
/**
* 已发布状态类
*/
public class PublishedState extends AbstractState {
@Override
public void acceptOrder(Context context) {
context.setState(new NotPaidState());
}
@Override
public void acceptOrderFail(Context context) {
context.setState(new FinishedState());
}
@Override
public String getCurrentState() {
return StateEnum.PUBLISHED.name();
}
}
/**
* 未付款状态类
*/
public class NotPaidState extends AbstractState {
@Override
public void payMoney(Context context) {
context.setState(new PaidState());
}
@Override
public void feedback(Context context) {
context.setState(new FinishedState());
}
@Override
public String getCurrentState() {
return StateEnum.NOT_PAID.name();
}
}
/**
* 未付款状态类
*/
public class PaidState extends AbstractState {
@Override
public void feedback(Context context) {
context.setState(new FinishedState());
}
@Override
public String getCurrentState() {
return StateEnum.PAID.name();
}
}
/**
* 未付款状态类
*/
public class FinishedState extends AbstractState {
@Override
public String getCurrentState() {
return StateEnum.FINISHED.name();
}
}
状态枚举类
public enum StateEnum {
GENERATED,
AUDITED,
PUBLISHED,
NOT_PAID,
PAID,
FINISHED;
}
上下文环境类
/**
* 上下文环境类
*/
public class Context extends AbstractState {
private State state;
public Context() {
state = new GeneratedState();
}
@Override
public void electronicAudit(Context context) {
state.electronicAudit(context);
getCurrentState();
}
@Override
public void electronicAuditFail(Context context) {
state.electronicAuditFail(context);
getCurrentState();
}
@Override
public void releasePricing(Context context) {
state.releasePricing(context);
getCurrentState();
}
@Override
public void acceptOrder(Context context) {
state.acceptOrder(context);
getCurrentState();
}
@Override
public void acceptOrderFail(Context context) {
state.acceptOrderFail(context);
getCurrentState();
}
@Override
public void payMoney(Context context) {
state.payMoney(context);
getCurrentState();
}
@Override
public void feedback(Context context) {
state.feedback(context);
getCurrentState();
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Override
public String getCurrentState() {
System.out.println("当前状态:" + state.getCurrentState());
return state.getCurrentState();
}
}
测试代码
Context context = new Context();
context.electronicAudit(context);
context.releasePricing(context);
context.acceptOrder(context);
context.payMoney(context);
context.electronicAuditFail(context);
context.acceptOrderFail(context);
//当前状态:AUDITED
//当前状态:PUBLISHED
//当前状态:NOT_PAID
//当前状态:PAID
//Exception in thread "main" java.lang.RuntimeException: 操作流程有误
// at com.vectorx.pattern.t20_state.lendingplatform.AbstractState.(AbstractState.java:7)
// at com.vectorx.pattern.t20_state.lendingplatform.Client.main(Client.java:5)
状态模式的注意事项和细节
优点
if-else
语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else
语句,而且容易出错缺点
会产生很多类:每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
应用场景
当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为时,可以考虑使用状态模式
鸭子问题
编写鸭子项目,具体要求如下:
传统方案解决鸭子问题
UML 类图
核心代码
public abstract class Duck {
public void quark() {
System.out.println("鸭子嘎嘎叫~");
}
public void swim() {
System.out.println("鸭子哗哗游~");
}
public void fly() {
System.out.println("鸭子腾腾飞~");
}
public abstract void display();
}
public class WildDuck extends Duck {
@Override
public void display() {
System.out.println("野鸭子");
}
}
public class PekingDuck extends Duck {
@Override
public void display() {
System.out.println("北京鸭~");
}
@Override
public void fly() {
System.out.println("北京鸭不会飞~");
}
}
public class ToyDuck extends Duck {
@Override
public void display() {
System.out.println("玩具鸭~");
}
@Override
public void quark() {
System.out.println("玩具鸭不会叫~");
}
@Override
public void swim() {
System.out.println("玩具鸭不会游~");
}
@Override
public void fly() {
System.out.println("玩具鸭不会飞~");
}
}
传统的方式实现的问题分析和解决方案
●1)其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
●2)上面说的问题,其实是继承带来的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分,会有溢出效应
●3)为了改进问题,我们可以通过覆盖fly方法来解决 => 覆盖解决
●4)问题又来了,如果我们有一个玩具鸭子ToyDuck,这样就需要ToyDuck去覆盖Duck的所有实现的方法 => 解决思路:策略模式
策略模式基本介绍
●1)策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换。此模式让算法的变化独立于使用算法的客户
●2)这算法体现了几个设计原则
○第一、把变化的代码从不变的代码中分离出来
○第二、针对接口编程而不是具体类(定义了策略接口)
○第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)
原理类图
说明:从上图可以看到,客户Context有成员变量Strategy或者其他的策略接口。至于需要使用到哪个策略,可以在构造器中指定
策略模式解决鸭子问题
应用实例要求:编写程序完成前面的鸭子项目,要求使用策略模式
思路分析
核心代码
“叫”的行为
/**
* “叫”行为策略接口
*/
public interface QuarkBehavior {
void quark();
}
/**
* “不会叫”行为策略对象
*/
public class NoQuarkBehavior implements QuarkBehavior {
@Override
public void quark() {
System.out.println("不会叫~");
}
}
/**
* “嘎嘎叫”行为策略对象
*/
public class GagaQuarkBehavior implements QuarkBehavior {
@Override
public void quark() {
System.out.println("嘎嘎叫~");
}
}
/**
* “咯咯叫”行为策略对象
*/
public class GegeQuarkBehavior implements QuarkBehavior {
@Override
public void quark() {
System.out.println("咯咯叫~");
}
}
“游泳”的行为
/**
* ”游泳“行为策略接口
*/
public interface SwimBehavior {
void swim();
}
/**
* “不会游泳”行为策略对象
*/
public class NoSwimHehavior implements SwimBehavior {
@Override
public void swim() {
System.out.println("不会游泳~");
}
}
/**
* “会游泳”行为策略对象
*/
public class CanSwimHehavior implements SwimBehavior {
@Override
public void swim() {
System.out.println("会游泳~");
}
}
“飞”的行为
/**
* “飞行”行为策略接口
*/
public interface FlyBehavior {
void fly();
}
/**
* “不会飞”行为策略对象
*/
public class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("不会飞~");
}
}
/**
* “不太会飞”行为策略对象
*/
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("不太会飞~");
}
}
/**
* “很会飞”行为策略对象
*/
public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println("很会飞~");
}
}
鸭子类
/**
* 抽象鸭子类
*/
public abstract class Duck {
protected QuarkBehavior quarkBehavior;
protected SwimBehavior swimBehavior;
protected FlyBehavior flyBehavior;
public Duck() {
display();
}
public void quark() {
if (quarkBehavior != null) {
quarkBehavior.quark();
}
}
public void swim() {
if (swimBehavior != null) {
swimBehavior.swim();
}
}
public void fly() {
if (flyBehavior != null) {
flyBehavior.fly();
}
}
public void setQuarkBehavior(QuarkBehavior quarkBehavior) {
this.quarkBehavior = quarkBehavior;
}
public void setSwimBehavior(SwimBehavior swimBehavior) {
this.swimBehavior = swimBehavior;
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public abstract void display();
}
/**
* 野鸭子
*/
public class WildDuck extends Duck {
public WildDuck() {
super();
quarkBehavior = new GegeQuarkBehavior();
swimBehavior = new CanSwimHehavior();
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
System.out.println("======野鸭子======");
}
}
/**
* 北京鸭
*/
public class PekingDuck extends Duck {
public PekingDuck() {
super();
quarkBehavior = new GagaQuarkBehavior();
swimBehavior = new CanSwimHehavior();
flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
System.out.println("======北京鸭======");
}
}
/**
* 玩具鸭
*/
public class ToyDuck extends Duck {
public ToyDuck() {
super();
quarkBehavior = new NoQuarkBehavior();
swimBehavior = new NoSwimHehavior();
flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
System.out.println("======玩具鸭======");
}
}
测试代码
Duck wildDuck = new WildDuck();
wildDuck.quark();
wildDuck.swim();
wildDuck.fly();
Duck pekingDuck = new PekingDuck();
pekingDuck.quark();
pekingDuck.swim();
pekingDuck.fly();
System.out.println("===改变策略===");
pekingDuck.setFlyBehavior(new NoFlyBehavior());
pekingDuck.fly();
Duck toyDuck = new ToyDuck();
toyDuck.quark();
toyDuck.swim();
toyDuck.fly();
测试结果
//======野鸭子======
//咯咯叫~
//会游泳~
//很会飞~
//======北京鸭======
//嘎嘎叫~
//会游泳~
//不太会飞~
//===改变策略===
//不会飞~
//======玩具鸭======
//不会叫~
//不会游泳~
//不会飞~
策略模式的注意事项和细节
●1)策略模式的关键是:分析项目中变化部分与不变部分
●2)策略模式的核心思想是:多用组合/聚合,少用继承;用行为类组合,而不是行为的继承,更有弹性
●3)体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if...else if...else)
●4)提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中,使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展
●5)需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
OA系统的采购审批项目
学校 OA 系统的采购审批项目,需求是
●1)采购员采购教学器材
●2)如果金额小于等于 5000,由教学主任审批(0 < x ≤ 5000)
●3)如果金额小于等于 10000,由院长审批(5000 < x ≤ 10000)
●4)如果金额小于等于 30000,由副校长审批(10000< x ≤ 30000)
●5)如果金额超过 30000 以上,有校长审批(30000 < x)
请设计程序完成采购审批项目
传统方案解决 OA 系统审批,传统的设计方案(类图)
传统方案解决 OA 系统审批问题分析
●1)传统方式是:接收到一个采购请求后,根据采购金额来调用对应的Approver(审批人)完成审批
●2)传统方式的问题分析:客户端这里会使用到分支判断(比如switch)来对不同的采购请求处理,这样就存在如下问题
○(1)如果各个级别的人员审批金额发生变化,在客户端的也需要变化
○(2)客户端必须明确的知道有多少个审批级别和访问
●3)这样对一个采购请求进行处理和Approver(审批人)就存在强耦合关系,不利于代码的扩展和维护
●4)解决方案 =》职责链模式
职责链模式基本介绍
● 1)职责链模式(Chain of Responsibility Pattern),又叫责任链模式:为请求创建了一个接收者对象的链(简单示意图)。
这种模式对请求的发送者和接收者进行解耦
● 2)职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推
● 3)这种类型的设计模式属于行为型模式
原理类图
职责链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系
将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
●Handler抽象处理者:定义了一个处理请求的方法,同时含有另外一个Handler
●ConcreteHandler具体处理者:处理自己负责的请求,同时可以访问它的后继者(即下一个处理者) ;如果可以处理请求,则进行处理,否则交给后继者去处理,从而形成一个职责链
●Request含有很多属性,表示一个请求
职责链模式解决 OA 系统采购审批项目
UML 类图
核心代码
请求类
/**
* 采购申请类
*/
public class PurchaseRequest {
private Integer id;
private Float price;
public PurchaseRequest(Integer id, Float price) {
this.id = id;
this.price = price;
}
public Integer getId() {
return id;
}
public Float getPrice() {
return price;
}
}
抽象审批人对象
/**
* 抽象审批人对象
*/
public abstract class Approver {
protected Approver nextApprover;
protected String name;
public Approver(String name) {
this.name = name;
}
/**
* 设置后继者
*
* @param nextApprover
*/
public void setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
}
/**
* 处理请求的方法
*/
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
具体审批人对象
/**
* 教学主任审批人
*/
public class TeachDirectorApprover extends Approver {
public TeachDirectorApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() <= 5000) {
System.out.println("请求编号:" + purchaseRequest.getId() + ",处理人:" + this.name);
} else {
nextApprover.processRequest(purchaseRequest);
}
}
}
/**
* 院长审批人
*/
public class DepartmentHeadApprover extends Approver {
public DepartmentHeadApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println("请求编号:" + purchaseRequest.getId() + ",处理人:" + this.name);
} else {
nextApprover.processRequest(purchaseRequest);
}
}
}
/**
* 副校长审批人
*/
public class ViceChancellorApprover extends Approver {
public ViceChancellorApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println("请求编号:" + purchaseRequest.getId() + ",处理人:" + this.name);
} else {
nextApprover.processRequest(purchaseRequest);
}
}
}
/**
* 副校长审批人
*/
public class ChancellorApprover extends Approver {
public ChancellorApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 30000) {
System.out.println("请求编号:" + purchaseRequest.getId() + ",处理人:" + this.name);
} else {
nextApprover.processRequest(purchaseRequest);
}
}
}
测试代码
//创建一个请求
PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000.0f);
//创建相关的审批人
TeachDirectorApprover teachDirectorApprover = new TeachDirectorApprover("童主任");
DepartmentHeadApprover departmentHeadApprover = new DepartmentHeadApprover("王院长");
ViceChancellorApprover viceChancellorApprover = new ViceChancellorApprover("钱副校长");
ChancellorApprover chancellorApprover = new ChancellorApprover("郑校长");
//设置后继者(处理人形成环形)
teachDirectorApprover.setNextApprover(departmentHeadApprover);
departmentHeadApprover.setNextApprover(viceChancellorApprover);
viceChancellorApprover.setNextApprover(chancellorApprover);
chancellorApprover.setNextApprover(teachDirectorApprover);
//发起一个请求
teachDirectorApprover.processRequest(purchaseRequest); //请求编号:1,处理人:郑校长
职责链模式的注意事项和细节
●1)将请求和处理分开,实现解耦,提高系统的灵活性
●2)简化了对象,使对象不需要知道链的结构
●3)性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
●4)调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
●5)最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假 / 加薪等审批流程、Java Web 中 Tomcat 对Encoding的处理、拦截器