目录
1.总体来说设计模式分为三大类:创建型模式,行为型模式,结构型模式
结论:从对象创建的角度上来说,原型模式设计让相似的类实例创建更加的便捷,由于这里的绵羊类较少,所以效果并不明显
5.职责链模式(Chain of Responsibility)
一.什么是设计模式?设计模式可以用在哪里?
设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。这个术语的含义还存有争议。算法不是设计模式,因为算法致力于解决问题而非设计问题。设计模式通常描述了一组相互紧密作用的类与对象。设计模式提供一种讨论软件设计的公共语言,使得熟练设计者的设计经验可以被初学者和其他设计者掌握。设计模式还为软件重构提供了目标。
随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且Ward Cunningham为此发明了WikiWiki用来交流设计模式的经验。
二.设计模式的分类
1.总体来说设计模式分为三大类:创建型模式,行为型模式,结构型模式
2.这三种又可以分为以下23种类型:
-
创建型:
1、单例模式(Singleton):保证一个类只有一个实例,并提供一个访问它的全局访问点 2、抽象工厂(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。 3、工厂方法(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。 4、建造模式(Builder):将一个复杂对象的构建与他的表示相分离,使得同样的构建过程可以创建不同的表示。 5、原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
-
行为型:
- 6、迭代器模式(Iterator):提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。
7、观察者模式(Observer):定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。
8、模板方法(Template Method):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,TemplateMethod使得子类可以不改变一个算法的结构即可以重定义该算法得某些特定步骤。
9、命令模式(Command):将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。
10、状态模式(State):允许对象在其内部状态改变时改变他的行为。对象看起来似乎改变了他的类。
11、策略模式(Strategy):定义一系列的算法,把他们一个个封装起来,并使他们可以互相替换,本模式使得算法可以独立于使用它们的客户。
12、职责链模式(China of Responsibility):使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系
13、中介者模式(Mediator):用一个中介对象封装一些列的对象交互。
14、访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。
15、解释器模式(Interpreter):给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
16、备忘录模式(Memento):在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 -
结构型:
- 17、组合模式(Composite):将对象组合成树形结构以表示部分整体的关系,Composite使得用户对单个对象和组合对象的使用具有一致性。
18、外观模式(Facade):为子系统中的一组接口提供一致的界面,fa?ade提供了一高层接口,这个接口使得子系统更容易使用。
19、代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问
20、适配器模式(Adapter):将一类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。
21、装饰模式(Decrator):动态地给一个对象增加一些额外的职责,就增加的功能来说,Decorator模式相比生成子类更加灵活。
22、桥接模式(Bridge):将抽象部分与它的实现部分相分离,使他们可以独立的变化。
23、享元模式(Flyweight)
三.各种模式是什么?怎么去使用各种模式?
创建型模式
1.原型模式(Prototype)是什么模式?
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象
在这里我们举个例子,假设我们要为学校设计每个星期的课表,学校共有十二个教室,十二个教员,那么问题来了,我们要怎么去设计这个课表呢?按照常规想法,每个星期有七天,每天有三个时间段,每个教室对应一个教员,因此我们应该实例化42个对象,可是这样就产生了很多弊端,比如代码量十分多,而且重复代码很多,那么这时我们就需要使用原型模式来解决这个问题,因为原型模式说的通俗一点就是拷贝相同代码。
那么真正的案例来了!!
需求:将一只名字为杰克、性别为母的绵羊克隆10份;
要求每只绵羊的属性、性别都一致;
- 使用原型模式前
package com.javaxl.design.prototype.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Sheep {
private String name;
private String sex;
public Sheep(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
package com.javaxl.design.prototype.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 将一只名字为杰克、性别为母的绵羊克隆10份;
* 要求每只绵羊的属性、性别都一致;
*
* 弊端:无法将当前的状态进行复制
*/
public class Client {
public static void main(String[] args) {
Sheep sheep1 = new Sheep("杰西", "母");
Sheep sheep2 = new Sheep("杰西", "母");
Sheep sheep3 = new Sheep("杰西", "母");
Sheep sheep4 = new Sheep("杰西", "母");
Sheep sheep5 = new Sheep("杰西", "母");
Sheep sheep6 = new Sheep("杰西", "母");
Sheep sheep7 = new Sheep("杰西", "母");
Sheep sheep8 = new Sheep("杰西", "母");
Sheep sheep9 = new Sheep("杰西", "母");
Sheep sheep10 = new Sheep("杰西", "母");
// 此时我要一只名为杰瑞的绵羊,其它绵羊属性与杰西一致;
// 那么按照这种设计,只能这么创建所需的绵羊
// 这种方式创建,目前只有两个属性问题不大,如果绵羊类有十几二十甚至更多的属性,那么是非常不方便的
Sheep sheep11 = new Sheep("杰瑞", "母");
}
}
- 使用原型模式后
-
package com.javaxl.design.prototype.after; /** * @author 钟羽 * @site www.javaxl.com * @company * 使用原型设计模式进行设计 */ public class Sheep implements Cloneable{ private String name; private String sex; public Sheep(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); System.out.println("被克隆了..."); return obj; } } package com.javaxl.design.prototype.after; /** * @author 钟羽 * @site www.javaxl.com * @company * 将一只名字为杰克、性别为母的绵羊克隆10份; * 要求每只绵羊的属性、性别都一致; * * 使用原型设计模式进行设计后的测试 */ public class Client { public static void main(String[] args) throws Exception{ Sheep sheep1 = new Sheep("杰西", "母"); Sheep sheep2 = (Sheep) sheep1.clone(); Sheep sheep3 = (Sheep) sheep1.clone(); Sheep sheep4 = (Sheep) sheep1.clone(); Sheep sheep5 = (Sheep) sheep1.clone(); Sheep sheep6 = (Sheep) sheep1.clone(); Sheep sheep7 = (Sheep) sheep1.clone(); Sheep sheep8 = (Sheep) sheep1.clone(); Sheep sheep9 = (Sheep) sheep1.clone(); Sheep sheep10 = (Sheep) sheep1.clone(); System.out.println(sheep1); System.out.println(sheep2); // 此时我要一只名为杰瑞的绵羊,其它绵羊属性与杰西一致; // 按照原型设计模式,调用方Client类无需查找杰西相同部分的属性,只需变动差异部分属性进行克隆即可; // 这种设计,目前只有两个属性使用起来感觉没多大区别,如果绵羊类有十几二十甚至更多的属性,那么感觉非常明显 sheep1.setName("杰瑞");//其它的属性不需要去关注 Sheep sheep11 = (Sheep) sheep1.clone(); System.out.println(sheep11); } }
结论:从对象创建的角度上来说,原型模式设计让相似的类实例创建更加的便捷,由于这里的绵羊类较少,所以效果并不明显
- 此处很容易遇见一个开发中的陷阱
-
public class Demo1 { // public static void main(String[] args) { // HashMap hm1 = new HashMap(); // hm1.put("name","zs"); // hm1.put("sex","女"); // HashMap hm2 = hm1; // hm1.put("age","18"); // hm2.put("like","男"); // System.out.println(hm1); // System.out.println(hm2); // } //这里hm1和hm2输出是一样的 public static void main(String[] args) { HashMap hm1 = new HashMap(); hm1.put("name","zs"); hm1.put("sex","女"); HashMap hm2 = (HashMap) hm1.clone(); hm1.put("age","18"); hm2.put("like","男"); System.out.println(hm1); System.out.println(hm2); //这里hm1和hm2输出是不一样的 } }
-
那么我们来加深一下,了解一下浅拷贝和深拷贝
-
浅拷贝
-
特点:对象中实例变量,如果是引用变量,不会重新开辟空间
-
package com.javaxl.design.prototype.light; /** * @author 钟羽 * @site www.javaxl.com * @company * * 使用原型设计模式进行设计 */ public class Sheep implements Cloneable{ private String name; private String sex; private Sheep friend; public Sheep(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Sheep getFriend() { return friend; } public void setFriend(Sheep friend) { this.friend = friend; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", friend=" + friend + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); System.out.println("被克隆了..."); return obj; } }
深拷贝:
-
方式一:
-
package com.javaxl.design.prototype.deep.one; /** * @author 钟羽 * @site www.javaxl.com * @company * <p> * 使用原型设计模式进行设计 */ public class Sheep implements Cloneable { private String name; private String sex; private Sheep friend; private Sheep boyFriend; public Sheep(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Sheep getFriend() { return friend; } public void setFriend(Sheep friend) { this.friend = friend; } public Sheep getBoyFriend() { return boyFriend; } public void setBoyFriend(Sheep boyFriend) { this.boyFriend = boyFriend; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", friend=" + friend + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); if (obj == null) { return obj; } else { // 被克隆出来的绵羊(目前这只绵羊的朋友还是本体的朋友) Sheep sheep = (Sheep) obj; // 将本体的朋友也克隆一份出来,给克隆羊 Sheep friend = this.getFriend(); if (friend !=null){ sheep.setFriend((Sheep) friend.clone()); } return sheep; } } } package com.javaxl.design.prototype.deep.one; /** * @author 钟羽 * @site www.javaxl.com * @company * <p> * 将一只名字为杰克、性别为母的绵羊克隆10份; * 要求每只绵羊的属性、性别都一致; * <p> * 使用原型设计模式进行设计后的测试 */ public class Client { public static void main(String[] args) throws Exception { Sheep sheep1 = new Sheep("杰西", "母"); Sheep friend_jiexi = new Sheep("杰西的朋友", "公"); Sheep boyFriend_jiexi = new Sheep("杰西的男朋友", "公"); sheep1.setFriend(friend_jiexi); sheep1.setBoyFriend(boyFriend_jiexi); Sheep sheep2 = (Sheep) sheep1.clone(); System.out.println("第1只叫杰西的绵羊的朋友:" + sheep1.getFriend().hashCode()); System.out.println("第2只叫杰西的绵羊的朋友:" + sheep2.getFriend().hashCode()); System.out.println("第1只叫杰西的绵羊的男朋友:" + sheep1.getBoyFriend().hashCode()); System.out.println("第2只叫杰西的绵羊的男朋友:" + sheep2.getBoyFriend().hashCode()); } }
-
第二种方式:
-
package com.javaxl.design.prototype.deep.two; import java.io.*; /** * @author 钟羽 * @site www.javaxl.com * @company * * 使用原型设计模式进行设计 */ public class Sheep implements Cloneable,Serializable{ private String name; private String sex; private Sheep friend; private Sheep boyFriend; public Sheep getBoyFriend() { return boyFriend; } public void setBoyFriend(Sheep boyFriend) { this.boyFriend = boyFriend; } public Sheep(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Sheep getFriend() { return friend; } public void setFriend(Sheep friend) { this.friend = friend; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", friend=" + friend + '}'; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); System.out.println("被克隆了..."); return obj; } protected Object deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException { // 序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 获取对象输出流 ObjectOutputStream oos = new ObjectOutputStream(bos); // 将当前的对象 oos.writeObject(this); // 反序列化 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); Object obj = ois.readObject(); return obj; } } public class Client { public static void main(String[] args) throws Exception { Sheep sheep1 = new Sheep("杰西", "母"); Sheep friend_jiexi = new Sheep("杰西的朋友", "公"); Sheep boyFriend_jiexi = new Sheep("杰西的男朋友", "公"); sheep1.setFriend(friend_jiexi); sheep1.setBoyFriend(boyFriend_jiexi); Sheep sheep2 = (Sheep) sheep1.deepClone(); System.out.println("第1只叫杰西的绵羊的朋友:" + sheep1.getFriend().hashCode()); System.out.println("第2只叫杰西的绵羊的朋友:" + sheep2.getFriend().hashCode()); System.out.println("第1只叫杰西的绵羊的男朋友:" + sheep1.getBoyFriend().hashCode()); System.out.println("第2只叫杰西的绵羊的男朋友:" + sheep2.getBoyFriend().hashCode()); } }
-
结论:推荐使用深拷贝的第二种方式
注意事项和细节
1.创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2.不用重新初始化对象,而是动态地获得对象运行时的状态
3.如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
缺点: 1、需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则
2、实现深克隆的时候可能需要比较复杂的代码
应用:树形节点的拖拽删除;
应用场景
-
单例模式的弊端
-
vue实现教务排课功能
-
原型模式的应用
-
vue实现教务排课解决单例带来的问题
2.单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
进入案例!!!
这里以经典的数据库驱动连接工具类为例;
数据库连接工具类在各个业务的dao层会被初始化调用;
而每一次初始化都会在堆内存中申请一片空间,这是对堆内存资源的浪费;
如果在堆内存只开辟一片空间,各个业务的Dao层建立不同的引用进行操作,就可以资源利用最大化;
单例设计模式就应运而生,它就是为了解决对象初始化,造成资源浪费的情况;
2.1 分类
-
饿汉式(静态常量)
-
饿汉式(静态代码块)
-
懒汉式(线程不安全)
-
懒汉式(线程安全,同步代码块)
-
懒汉式(线程安全,同步方法)
-
双重检查
-
静态内部类
-
枚举
package com.javaxl.design.single.after; /** * @author 钟羽 * @site www.javaxl.com * @company * 饿汉式(静态常量) */ public class DBAccess { // 构造器私有化,避免外部创建对象 private DBAccess() { } // static修饰,保障其能够被静态方法访问 private final static DBAccess dbAccess = new DBAccess(); // 外部直接调用静态方法实例化对象 public static DBAccess getInstance() { return dbAccess; } } /** * 饿汉式(静态代码块) */ class DBAccess2 { private DBAccess2() { } private static DBAccess2 dbAccess = null; static { dbAccess = new DBAccess2(); } public static DBAccess2 getInstance() { return dbAccess; } } /** * 懒汉式(线程不安全) */ class DBAccess3 { private DBAccess3() { } private static DBAccess3 dbAccess = null; public static DBAccess3 getInstance() { if (dbAccess == null) { dbAccess = new DBAccess3(); } return dbAccess; } } /** * 懒汉式(同步代码块) */ class DBAccess4 { private DBAccess4() { } private static DBAccess4 dbAccess = null; public static DBAccess4 getInstance() { synchronized (DBAccess4.class) { if (dbAccess == null) { dbAccess = new DBAccess4(); } } return dbAccess; } } /** * 懒汉式(线程安全,同步方法) */ class DBAccess5 { private DBAccess5() { } private static DBAccess5 dbAccess = null; public synchronized static DBAccess5 getInstance() { if (dbAccess == null) { dbAccess = new DBAccess5(); } return dbAccess; } } /** * 双重检查 */ class DBAccess6 { private DBAccess6() { } private static DBAccess6 dbAccess = null; public static DBAccess6 getInstance() { if (dbAccess == null) { synchronized (DBAccess6.class) { if (dbAccess == null) { dbAccess = new DBAccess6(); } } } return dbAccess; // return new DBAccess6(); } } /** * 静态内部类 */ class DBAccess7 { private DBAccess7() { } private static class DBAccess7Instance{ private static DBAccess7 dbAccess = new DBAccess7(); } public static DBAccess7 getInstance() { return DBAccess7Instance.dbAccess; } } /** * 枚举 */ enum DBAccess8{ DBACCESS; public static DBAccess8 getInstance() { return DBAccess8.DBACCESS; } }
结论:
1.单例中两种饿汉式可用,但是存在性能问题
2.单例中三种懒汉式不推荐,存在线程安全问题,同步方法的方式解决了线程的问题,但是性能极差
3.最后三种单例模式值得推荐
注意事项
-
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
-
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
应用
-
jdk源码中Runtime类
-
tomcat中ApplicationContext类
-
session 工厂
2.2 Spring单例多例模式的演示、区别及选择
package com.zking.single.demo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zking.ioc.web.ParamAction;
/**
* Spring单例多例模式的演示、区别及选择
*
* @author Administrator
*
*/
public class Demo1 {
// scope="singleton" 默认的
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
System.out.println(p1 == p2);
}
// scope="prototype"
@Test
public void test2() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
System.out.println(p1 == p2);
}
// 区别:单例多例的选择?
@Test
public void test3() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
p1.execute();
p2.execute();
}
}
3.工厂模式(Factory)
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
有趣的案例又来啦!!
需求:一个披萨制作的项目,要求该项目易于扩展维护;
1、能够生产出美式披萨、中式披萨...
2、披萨制作过程包含原材料准备、烘培、切割、打包
3、可生成披萨订单
使用工厂模式前
package com.javaxl.design.factory;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public interface Pizza {
void pre();
void bake();
void cut();
void box();
}
/**
* 中式披萨
*/
class ChinesePizza implements Pizza {
public ChinesePizza() {
this.pre();
this.bake();
this.cut();
this.box();
}
@Override
public void pre() {
System.out.println("中式披萨材料准备...");
}
@Override
public void bake() {
System.out.println("中式披萨烘培...");
}
@Override
public void cut() {
System.out.println("中式披萨的切片...");
}
@Override
public void box() {
System.out.println("中式披萨包装盒包装");
}
}
/**
* 美式披萨
*/
class AmericaPizza implements Pizza {
public AmericaPizza() {
this.pre();
this.bake();
this.cut();
this.box();
}
@Override
public void pre() {
System.out.println("美式 披萨材料准备...");
}
@Override
public void bake() {
System.out.println("美式 披萨烘培...");
}
@Override
public void cut() {
System.out.println("美式 披萨的切片...");
}
@Override
public void box() {
System.out.println("美式 披萨包装盒包装");
}
}
class JapanPizza implements Pizza {
public JapanPizza() {
this.pre();
this.bake();
this.cut();
this.box();
}
@Override
public void pre() {
System.out.println("日式 披萨材料准备...");
}
@Override
public void bake() {
System.out.println("日式 披萨烘培...");
}
@Override
public void cut() {
System.out.println("日式 披萨的切片...");
}
@Override
public void box() {
System.out.println("日式 披萨包装盒包装");
}
}
public class PizzaFactory {
public static Pizza createPizza(int no){
switch (no){
case 1:
return new ChinesePizza();
case 2:
return new AmericaPizza();
case 3:
return new JapanPizza();
}
return null;
}
}
public class Client {
public static void main(String[] args) {
Pizza chinaPizza = PizzaFactory.createPizza(1);
System.out.println("=============================");
Pizza americaPizza = PizzaFactory.createPizza(2);
System.out.println("=============================");
Pizza japanPizza = PizzaFactory.createPizza(3);
}
}
这样写看起来是没有问题的,但是如果披萨出现了半成品,也就是说前面的两个步骤不需要了,那么半成品披萨实现披萨的话就会出现问题,所以这里我们就需要使用工厂模式
使用后
package com.javaxl.design.factory.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public interface Pizza {
void pre();
void bake();
void cut();
void box();
}
/**
* 中式披萨
*/
class ChinesePizza implements Pizza {
@Override
public void pre() {
System.out.println("中式披萨材料准备...");
}
@Override
public void bake() {
System.out.println("中式披萨烘培...");
}
@Override
public void cut() {
System.out.println("中式披萨的切片...");
}
@Override
public void box() {
System.out.println("中式披萨包装盒包装");
}
}
/**
* 美式披萨
*/
class AmericaPizza implements Pizza {
@Override
public void pre() {
System.out.println("美式 披萨材料准备...");
}
@Override
public void bake() {
System.out.println("美式 披萨烘培...");
}
@Override
public void cut() {
System.out.println("美式 披萨的切片...");
}
@Override
public void box() {
System.out.println("美式 披萨包装盒包装");
}
}
class JapanPizza implements Pizza {
@Override
public void pre() {
System.out.println("日式 披萨材料准备...");
}
@Override
public void bake() {
System.out.println("日式 披萨烘培...");
}
@Override
public void cut() {
System.out.println("日式 披萨的切片...");
}
@Override
public void box() {
System.out.println("日式 披萨包装盒包装");
}
}
public class PizzaFactory {
public static Pizza createPizza(int no){
Pizza pizza = null;
switch (no){
case 1:
pizza = new ChinesePizza();
break;
case 2:
pizza = new AmericaPizza();
break;
case 3:
pizza = new JapanPizza();
break;
}
if (pizza == null){
System.out.println("没有此披萨订购服务");
}else {
pizza.pre();
pizza.bake();
pizza.cut();
pizza.box();
}
return pizza;
}
}
public class Client {
public static void main(String[] args) {
Pizza chinaPizza = PizzaFactory.createPizza(1);
System.out.println("=============================");
Pizza americaPizza = PizzaFactory.createPizza(2);
System.out.println("=============================");
Pizza japanPizza = PizzaFactory.createPizza(3);
System.out.println("=============================");
Pizza otherPizza = PizzaFactory.createPizza(4);
}
}
使用前后代码对比:
1.前者将对象初始化的工作交给了对象的构造函数完成;
2.后者将初始化的过程交给了工厂类完成;
3.这么做的好处在于解耦自身对象实例化和对象初始化动作,还有一个因素在于构造函数的初始化会影响到子类;
-
注意事项及细节
将对象的初始化交给工厂类构造函数初始化会影响到子类,耦合度过高
-
应用
-
简单工厂
xml建模jdk源码中Calendar类
-
抽象工厂
Hibernate框架Spring框架Mybatis框架Shiro框架
-
应用场景
Bean的生命周期(单例多例bean初始化源码剖析)
package com.zking.single.demo;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import com.zking.ioc.web.ParamAction;
/**
* Bean的生命周期
单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时Spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
*
* @author Administrator
*
*/
public class Demo2 {
// 单例对象与多例对象的初始化时间不一样 通过scope属性来演示
@Test
public void test1() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
}
// 单例对象与多例对象的初始化时间不一样 通过scope属性来演示
@Test
public void test2() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
InstanceFactory i1 = (InstanceFactory) applicationContext.getBean("instanceFactory");
i1.service();
// scope="prototype",容器关闭不会自动销毁bean对象
applicationContext.close();
}
/**
* 说明了两点:
* scope的值对应的是两个工厂类,生产javabean的模式一样;
* 1.一个是单例一个是多例
* 2.一个是立即初始化,一个是使用初始化
* 反正要初始化一次,干脆把所有的初始化操作放到监听器里面去,提高系统应用的性能
* 多例本身会耗性能,那么就尽可能在使用的时候再去创造对象
*/
@Test
public void test3() {
// ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
Resource resource = new ClassPathResource("/spring-context.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource );
InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
}
}
4.建造者模式(Builder Pattern)
定义:建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
案例来了!!!!
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
步骤 1
创建一个表示食物条目和食物包装的接口。
Item.java
public interface Item {
public String name();
public Packing packing();
public float price();
}
Packing.java
public interface Packing {
public String pack();
}
步骤 2
创建实现 Packing 接口的实体类。
Wrapper.java
public class Wrapper implements Packing {
@Override
public String pack() {
return "Wrapper";
}
}
Bottle.java
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
步骤 3
创建实现 Item 接口的抽象类,该类提供了默认的功能。
Burger.java
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float price();
}
ColdDrink.java
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
}
步骤 4
创建扩展了 Burger 和 ColdDrink 的实体类。
VegBurger.java
public class VegBurger extends Burger {
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "Veg Burger";
}
}
ChickenBurger.java
public class ChickenBurger extends Burger {
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "Chicken Burger";
}
}
Coke.java
public class Coke extends ColdDrink {
@Override
public float price() {
return 30.0f;
}
@Override
public String name() {
return "Coke";
}
}
Pepsi.java
public class Pepsi extends ColdDrink {
@Override
public float price() {
return 35.0f;
}
@Override
public String name() {
return "Pepsi";
}
}
步骤 5
创建一个 Meal 类,带有上面定义的 Item 对象。
Meal.java
import java.util.ArrayList;
import java.util.List;
public class Meal {
private List<Item> items = new ArrayList<Item>();
public void addItem(Item item){
items.add(item);
}
public float getCost(){
float cost = 0.0f;
for (Item item : items) {
cost += item.price();
}
return cost;
}
public void showItems(){
for (Item item : items) {
System.out.print("Item : "+item.name());
System.out.print(", Packing : "+item.packing().pack());
System.out.println(", Price : "+item.price());
}
}
}
步骤 6
创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。
MealBuilder.java
public class MealBuilder {
public Meal prepareVegMeal (){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
public Meal prepareNonVegMeal (){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
步骤 7
BuiderPatternDemo 使用 MealBuilder 来演示建造者模式(Builder Pattern)。
BuilderPatternDemo.java
public class BuilderPatternDemo {
public static void main(String[] args) {
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("Veg Meal");
vegMeal.showItems();
System.out.println("Total Cost: " +vegMeal.getCost());
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
System.out.println("\n\nNon-Veg Meal");
nonVegMeal.showItems();
System.out.println("Total Cost: " +nonVegMeal.getCost());
}
}
步骤 8
执行程序,输出结果:
Veg Meal Item : Veg Burger, Packing : Wrapper, Price : 25.0 Item : Coke, Packing : Bottle, Price : 30.0 Total Cost: 55.0 Non-Veg Meal Item : Chicken Burger, Packing : Wrapper, Price : 50.5 Item : Pepsi, Packing : Bottle, Price : 35.0 Total Cost: 85.5
5.抽象工厂模式
结构型模式
1.适配器模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题
案例来了!!
需求:电源适配的项目,要让手机能够使用220V的交流电;注意,手机只接受5.5V的电压;
角色
-
sourcePower220V===》220V的电源
-
AdapterPowerAdapt===》电源适配器
-
DestinationPower5V===》5V的电源
使用前
package com.javaxl.design.adapter.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 220V的电压
*/
public class Voltage220V {
private double voltage;
public Voltage220V() {
this.voltage = 220;
}
public double getVoltage() {
return voltage;
}
public void setVoltage(double voltage) {
this.voltage = voltage;
}
}
public class Phone {
// 充电
public void charge(Voltage220V voltage220V){
double voltage = voltage220V.getVoltage() / 40;
System.out.println("最终手机充电所用电压:" + voltage + "V");
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
// 已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
// 还一种方案:新增5V的一个Voltage5V,Voltage的电压可以被手机使用
// 但是这违背现实生活现象,现实生活中只有220V的电源,其他的电源都是通过适配得来的
phone.charge(new Voltage220V());
}
}
使用后
package com.javaxl.design.adapter.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 220V的电压
*/
public class Voltage220V {
private double voltage;
public Voltage220V() {
this.voltage = 220;
}
public double getVoltage() {
return voltage;
}
public void setVoltage(double voltage) {
this.voltage = voltage;
}
}
/**
* 目标接口
*/
interface Voltage5V{
double getVoltage();
}
/**
* 适配器:里面封装了source源到destination目标的过程
*/
class VoltageAdapter implements Voltage5V{
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public double getVoltage() {
return voltage220V.getVoltage() / 40;
}
}
package com.javaxl.design.adapter.after;
public class Phone {
// 充电
public void charge(Voltage5V voltage5V){
double voltage = voltage5V.getVoltage();
System.out.println("最终手机充电所用电压:" + voltage + "V");
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
// 已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
// VoltageAdapter适配器对Voltage220V这个不能直接使用的电源适配后就可以使用了
phone.charge(new VoltageAdapter(new Voltage220V()));
}
}
分类
-
类适配器模式Adapter 类,通过继承 source 类,实现 Destination 类接口,完成 source->Destination 的适配。
-
对象适配器模式将 Adapter 类作修改,不是继承 source 类,而是持有 source 类的实例,以解决兼容性的问题。 即:持有 source 类,实现 Destination 类接口,完成source->Destination 的适配
-
接口适配器模式当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求 适用于一个接口不想使用其所有的方法的情况
注意事项和细节
Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作
-
应用
SpringMvc框架中HandlerAdapter类安卓中监听适配器类中就使用了接口适配器(上拉、下拉...)
2.桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化
角色
-
Client 类:桥接模式的调用者
-
Abstraction:维护了 Implementor/ 即它的实现类 ConcreteImplementorA.., 二者是聚合关系,Abstraction充当桥接类
-
xxxAbstraction :抽象的具体子类
-
Implementor :行为实现类的接口
-
ConcreteImplementorA /B:具体行为的实现类A、B
注意事项及细节
实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
注意:1、桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程2、桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。
-
应用
JDBC的Driver驱动类
2.装饰者模式(Decorator)
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
案例来了!!!!
单体咖啡与调味组合的饮料计价项目
-
角色
-
Client 类:装饰模式的调用者
-
Component:主体===》饮料(Drink)
-
concreteComponent:被装饰者===》咖啡(Coffee)
-
xxxConcreteComponent:具体的被装饰者(ChinaCoffee)美式咖啡、欧式咖啡、中式咖啡
-
Decorator:装饰器===》调料
-
xxxDecorator:子装饰器糖类、奶酪、牛油、芥末
-
注意:concreteComponent、Decorator都会实现或继承Component
这个项目最容易想到的设计方案就是采用继承的设计方案,设计思路如下:
Drink===》饮品
Juice===》果汁
.......
Coffee===》咖啡
ChinaCoffee===》中式咖啡
ASeasoningChinaCoffee===》被A调料修饰的中式咖啡
BSeasoningChinaCoffee===》被B调料修饰的中式咖啡
......
XxxCoffee===》其它咖啡
ASeasoningXxxCoffee===》被A调料修饰的其它咖啡
BSeasoningXxxCoffee===》被B调料修饰的其它咖啡
......
上面每个类中都有getPrice计价的功能,从结果上来看,确实可以完成项目需求,但是整个设计体系过于臃肿,不便于后期的扩展与维护;
用前面的桥接模式来进行设计,可以解决体系臃肿问题
package com.javaxl.design.decorator.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 饮料包括单体咖啡+调料
*/
public abstract class Drink {
protected double price;
protected int n;
protected DrinkSeasoning seasoning;
public abstract double getPrice();
}
/**
* 单体咖啡
*/
abstract class Coffee extends Drink {
}
/**
* 单体果汁
*/
abstract class Juice extends Drink {
}
class ChinaCoffee extends Coffee{
ChinaCoffee(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n+this.seasoning.getPrice();
}
}
package com.javaxl.design.decorator.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
* 调料的抽象接口
*/
public interface DrinkSeasoning {
public abstract double getPrice();
}
/**
* A类调料
*/
class ADrinkSeasoning implements DrinkSeasoning{
protected double price;
protected int n;
ADrinkSeasoning(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n;
}
}
/**
* B类调料
*/
class BDrinkSeasoning implements DrinkSeasoning{
private double price;
protected int n;
BDrinkSeasoning(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n;
}
}
public class Client {
public static void main(String[] args) {
ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
ADrinkSeasoning aDrinkSeasoning = new ADrinkSeasoning(2,2);
chinaCoffee.seasoning = aDrinkSeasoning;
System.out.println("中式咖啡1份+A调料2份,最终价格为:"+chinaCoffee.getPrice());
// 思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
// 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
}
}
上面的代码存在问题:
思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢? seasoning只能代表一种调料
思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢? 原有的设计没办法应对这种场景
思考3:Drink饮品单体饮料种类多,调料种类也多,会带来什么问题?
Drink抽象类中聚合List<DrinkSeasoning>才可以解决上述的前两个问题;但是,在原有订单追加调料,相当于给原有对象进行装饰,这类的问题更加适合用装饰模式来解决;
使用装饰模式进行设计
package com.javaxl.design.decorator.after;
import com.javaxl.design.decorator.before.DrinkSeasoning;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
* <p>
* 饮料包括单体咖啡+调料
*/
public abstract class Drink {
protected double price;
protected int n;
public abstract double getPrice();
}
/**
* 单体咖啡
*/
abstract class Coffee extends Drink {
}
/**
* 单体果汁
*/
abstract class Juice extends Drink {
}
class ChinaCoffee extends Coffee {
ChinaCoffee(double price, int n) {
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price * this.n;
}
}
package com.javaxl.design.decorator.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class DecoratorDrink extends Drink {
private Drink drink;
public DecoratorDrink(Drink drink, double price, int n) {
this.drink = drink;
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price * this.n + drink.getPrice();
}
}
class ADecoratorDrink extends DecoratorDrink {
public ADecoratorDrink(Drink drink, double price, int n) {
super(drink, price, n);
}
}
class BDecoratorDrink extends DecoratorDrink {
public BDecoratorDrink(Drink drink, double price, int n) {
super(drink, price, n);
}
}
package com.javaxl.design.decorator.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Client {
public static void main(String[] args) {
ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
// 假定A类调料2元一份,B类调料3元一份
Drink order = new ADecoratorDrink(chinaCoffee, 2, 2);
System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice());
// 思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
order = new ADecoratorDrink(order,2,1);
System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice());
order = new BDecoratorDrink(order,3,2);
System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice());
// 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
order = new BDecoratorDrink(order,3,2);
System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice());
}
}
注意事项及细节
-
装饰者模式一般用于对原有功能进行增强/装饰
-
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
应用
IO流体系:缓冲流
3.外观模式(Facade)
定义: 隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口
简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,设计到3个角色。
1)门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。(客户调用,同时自身调用子系统功能)
2)子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。(实现具体功能)
3)客户角色:通过调用Facede来完成要实现的功能(调用门面角色)。
打个简单的比方,当我们到公司里面接手他人项目时,其他人的代码就是看不惯,但是又不愿意自己写的情况下,就可以另外创建一个类专门用来供给使用
案例来咯!!!
需求:组装一个家庭影院;
电脑打开、投影仪打开、音箱打开、灯光调暗、零食拿出来,电影开始;
零食收起来、灯光调亮、音箱关闭、投影仪关闭、电脑关闭,电影结束;
角色
Client 类 外观模式的调用者 ComponentA 子系统A 电脑ComponentB 子系统B 投影仪Facade HomeTheaterFacade 家庭影院
使用前
package com.javaxl.design.facade.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
*/
public class ComponentA {
public void m1(){
System.out.println("电脑功能一...");
}
public void m2(){
System.out.println("电脑功能二...");
}
public void on(){
System.out.println("电脑打开...");
}
public void off(){
System.out.println("电脑关闭...");
}
}
//投影仪
class ComponentB {
public void on(){
System.out.println("投影仪打开...");
}
public void off(){
System.out.println("投影仪关闭...");
}
}
//音箱
class ComponentC {
public void on(){
System.out.println("音箱打开...");
}
public void off(){
System.out.println("音箱关闭...");
}
}
//、灯光
class ComponentD {
public void on(){
System.out.println("灯光调亮...");
}
public void off(){
System.out.println("灯光调暗...");
}
}
//零食
class ComponenE {
public void on(){
System.out.println("零食拿出来...");
}
public void off(){
System.out.println("零食收起来...");
}
}
public class Client {
public static void main(String[] args) {
new ComponentA().on();
new ComponentB().on();
new ComponentC().on();
new ComponentD().off();
new ComponenE().on();
System.out.println("电影开始了...");
System.out.println();
new ComponenE().off();
new ComponentD().on();
new ComponentC().off();
new ComponentB().off();
new ComponentA().off();
System.out.println("电影结束了...");
}
}
从上面代码可以看出:
客户端调用依赖了所有的子系统(ABCDE),如果该需求反复出现,对于客户端调用而言,就不是很方便了;
另一方面,此需求完成只需要依赖各个子系统的其中一部分功能,其它功能客户端用不上,依照迪米特法则我们也应该解耦客户端与各个子系统的关系;
使用后
package com.javaxl.design.facade.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*
* 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
*/
public class ComponentA {
public void m1(){
System.out.println("电脑功能一...");
}
public void m2(){
System.out.println("电脑功能二...");
}
public void on(){
System.out.println("电脑打开...");
}
public void off(){
System.out.println("电脑关闭...");
}
}
//投影仪
class ComponentB {
public void on(){
System.out.println("投影仪打开...");
}
public void off(){
System.out.println("投影仪关闭...");
}
}
//音箱
class ComponentC {
public void on(){
System.out.println("音箱打开...");
}
public void off(){
System.out.println("音箱关闭...");
}
}
//、灯光
class ComponentD {
public void on(){
System.out.println("灯光调亮...");
}
public void off(){
System.out.println("灯光调暗...");
}
}
//零食
class ComponentE {
public void on(){
System.out.println("零食拿出来...");
}
public void off(){
System.out.println("零食收起来...");
}
}
public class ComponentFacade {
ComponentA componentA =new ComponentA();
ComponentB componentB = new ComponentB();
ComponentC componentC = new ComponentC();
ComponentD componentD = new ComponentD();
ComponentE componenE = new ComponentE();
public void on(){
componentA.on();
componentB.on();
componentC.on();
componentD.off();
componenE.on();
System.out.println("电影开始了...");
}
public void off(){
componenE.off();
componentD.on();
componentC.off();
componentB.off();
componentA.off();
System.out.println("电影结束了...");
}
}
public class Client {
public static void main(String[] args) {
ComponentFacade componentFacade = new ComponentFacade();
componentFacade.on();
System.out.println();
componentFacade.off();
}
}
从上面可以看出,客户端只依赖了外观类,彻底解耦了与各个子系统之间的关系;
注意事项及细节
-
屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
-
对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
-
当系统需要进行分层设计时,可以考虑使用 Facade 模式
应用
后期代码维护,提取某些类中可用的方法聚合在一起
3.代理模式(Proxy)
定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介
举个例子来说明:假如说我现在想买房,虽然我可以自己去找,但是这个过程太麻烦了,我只是想买房而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买,他们来给我找房源,帮我办理事务,我只是负责选择我需要的,然后付钱就可以了。用图表示如下:
代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理
静态代理
案例如下!!!
角色
接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy
细节 代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象
案例代码如下:
package com.javaxl.design.proxy.staticproxy;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
* <p>
* 目标类
*/
public class TeacherDAO implements ITeacherDao {
public void teach() {
System.out.println("老师传授知识");
}
}
//目标接口
interface ITeacherDao {
void teach();
}
//代理类
class TeacherDAOProxy implements ITeacherDao {
private ITeacherDao teacherDAO;
public TeacherDAOProxy(ITeacherDao teacherDAO) {
this.teacherDAO = teacherDAO;
}
@Override
public void teach() {
System.out.println("老师正式授课前的准备工作,如学生全部签到...");
teacherDAO.teach();
System.out.println("老师结束授课,如下课铃声响起...");
}
}
public class Client {
public static void main(String[] args) {
TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
proxy.teach();
}
}
动态代理
-
jdk代理
-
角色
-
接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy 细节 不需要实现接口,但是目标对象要实现接口,否则不能用动态代理 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象 代理类所在包:java.lang.reflect.Proxy
案例代码如下:
package com.javaxl.design.proxy.dynamic.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 目标接口
*/
interface ITeacherDao {
String teach();
ITeacherDao sleep(int minutes);
}
class TeacherDao implements ITeacherDao{
@Override
public String teach() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date())+":老师传授知识";
}
@Override
public ITeacherDao sleep(int minutes) {
System.out.println("老师睡了" + minutes + "分钟");
return this;
}
}
//真实代理类的外衣
class TeacherDaoProxy{
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
public Object xxx(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
String methodName = method.getName();
System.out.println("目标方法" + methodName + ":jdk代理开始...");
System.out.println("真实代理对象:"+proxy.getClass());
System.out.println("目标对象:"+target.getClass());
if("sleep".equals(methodName)){
// method.invoke(target, args);
// obj = proxy;
obj = method.invoke(target, args);
}else {
// proxy是真实代理类,method是目标方法,args是目标方法携带的参数
obj = method.invoke(target, args);
}
System.out.println("目标方法" + methodName + ":jdk代理结束...");
return obj;
}
});
}
}
public class Client {
public static void main(String[] args) {
TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
ITeacherDao ins = (ITeacherDao) proxy.xxx();
System.out.println("===========代理类实例被使用 begin=============");
System.out.println(ins);
System.out.println("===========代理类实例被使用 end=============");
System.out.println(ins.teach());
// System.out.println(proxy.execute());
System.out.println("===========代理类实例被使用 begin=============");
ins.sleep(10);
System.out.println("===========代理类实例被使用 end=============");
ins.sleep(20).sleep(60);
}
}
Cglib代理
角色 接口 ITeacherDao 目标对象 TeacherDAO 代理类 TeacherDAOProxy
细节
- 目标对象与代理对象都不需要实现接口
- Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展
- Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用 Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类
注意:
1)需要引入 cglib 的 jar 文件
2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
案例代码如下:
package com.javaxl.design.proxy.dynamic.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
class TeacherDao {
public String teach() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(new Date()) + ":老师传授知识";
}
public TeacherDao sleep(int minutes) {
System.out.println("老师睡了" + minutes + "分钟");
return this;
}
}
//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
private Object target;
public TeacherDaoProxy(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();
}
/**
* @param proxyIns 由CGLib动态生成的代理类实例
* @param method 上文中实体类所调用的被代理的方法引用
* @param args 参数值列表
* @param methodProxy 生成的代理类对方法的代理引用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
Object res;
System.out.println("目标方法" + methodName + ":cglib代理开始...");
System.out.println("真实代理对象:" + proxyIns.getClass());
System.out.println("目标对象:" + target.getClass());
if ("sleep".equals(methodName)) {
// method.invoke(target, args);
// obj = proxy;
res = method.invoke(target, args);
// res = methodProxy.invokeSuper(proxyIns,args);
res = proxyIns;
} else {
// proxy是真实代理类,method是目标方法,args是目标方法携带的参数
res = method.invoke(target, args);
}
System.out.println("目标方法" + methodName + ":cglib代理结束...");
return res;
}
}
public class Client {
public static void main(String[] args) {
TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
proxy.sleep(111).sleep(222);
}
}
jdk代理与Cglib代理比较:
JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
应用
Spring框架的AOP:Cglib代理的体现
行为型模式
1.模板方法模式(Template)
定义:定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤,通俗点说就是在父类中定义一个完成某事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法,每个步骤的具体实现,由子类完成
模板模式UML类图
案例说话!!!
需求:统计某一代码运行时间
-
使用前
package com.javaxl.design.template.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class CodeTotalTime {
public static void template(){
long start = System.currentTimeMillis();
// 检测Operation_1方法运行的时长======33
Operation_1();
// 检测Operation_2方法运行的时长======616
// Operation_2();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public static void Operation_1(){
for (int i = 0; i<1000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_1方法运行的时长======");
}
public static void Operation_2(){
for (int i = 0; i<20000 ;i++){
System.out.println("模拟耗时操作...");
}
System.out.print("检测Operation_2方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
CodeTotalTime.template();
}
}
使用模板模式后
abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
}
class ConcreteClassA extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 1000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("检测ConcreteClassA.method方法运行的时长======");
}
}
class ConcreteClassB extends CodeAbstractClass {
@Override
public void method() {
for (int i = 0; i < 20000; i++) {
System.out.println("模拟耗时操作...");
}
System.out.print("ConcreteClassB.method方法运行的时长======");
}
}
public class Client {
public static void main(String[] args) {
//检测ConcreteClassA.method方法运行的时长======当前方法执行时长:
new ConcreteClassA().template();
//ConcreteClassB.method方法运行的时长======当前方法执行时长:
new ConcreteClassB().template();
}
}
钩子函数应用场景:
public abstract class CodeAbstractClass {
public void template() {
long start = System.currentTimeMillis();
if (callback()) method();
long end = System.currentTimeMillis();
System.out.println("当前方法执行时长:" + (end - start));
}
public abstract void method();
public boolean callback() {
return true;
}
}
从上面可以看出:template方法默认是用作统计method方法的执行时长,但是有的时候我们无需统计代码时长,template函数中有一些其它逻辑要执行,在这里我们可以考虑采用钩子函数;钩子函数被子类覆写,覆写成false,那么method方法就不会被调用,不再统计代码时长了;前端框架Vue的生命周期就有多处用到钩子函数;
-
注意事项和细节
-
钩子函数在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”
-
算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
-
一般模板方法都加上 final 关键字, 防止子类重写模板方法
-
-
应用
Spring IOC容器加载
2.命令模式(Command)
定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令
标准UML类图
接下来是案例!!!
- 需求:万能遥控器的制作
-
角色
-
Command抽象命令 ( 执行命令 撤销命令)
-
ConcreteCommand LightOnCommand 开灯 LightOffCommand 关灯
NonComman 空命令
-
Invoker调用者
-
遥控器聚合所有命令
Command[] ons Command[] offs Command undo
Receiver接受者 电灯、空调、电视
案例UML类图
案例代码
/**
* * @author钟羽
* @site www.javaxl.com * @company * @create 2020-02-24 18:14
* * * 被操作的对象
*/
public class Light {
public void on() {
System.out.println("电灯打开...");
}
public void off() {
System.out.println("电灯关闭...");
}
}
class AirConditioner {
public void on() {
System.out.println("空调打开...");
}
public void off() {
System.out.println("空调关闭...");
}
}
class Television {
public void on() {
System.out.println("电视打开...");
}
public void off() {
System.out.println("电视关闭...");
}
}
/**
* * @author 钟羽
* @site www.javaxl.com * @company * @create 2020-02-24 18:25
*/
interface Command {
void execute();
void undo();
}// 空命令
class NonCommand implements Command {
@Override
public void execute() {
}
@Override
public void undo() {
}
}
class LightOnCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
class LightOffCommand implements Command {
private Light light = new Light();
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
class TvOnCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.on();
}
@Override
public void undo() {
tv.off();
}
}
class TvOffCommand implements Command {
private Television tv = new Television();
@Override
public void execute() {
tv.off();
}
@Override
public void undo() {
tv.on();
}
}
public class Invoker {
Command[] ons;
Command[] offs;// 记录上一个命令
Command command;
public Invoker(int n) {
ons = new Command[n];
offs = new Command[n];
command = new NonCommand();
for (int i = 0; i < n; i++) {
setCommand(i, new NonCommand(), new NonCommand());
}
}
public void setCommand(int no, Command on, Command off) {
ons[no] = on;
offs[no] = off;
}
public Command getOnCommand(int no) {
return ons[no];
}
public Command getOffCommand(int no) {
return offs[no];
}
// 执行命令
public void invoke(Command command) {
// 执行当前命令
command.execute();// 保存当前执行命令
this.command = command;
}// 撤销命令(上个操作的反操作)
public void undo() {// 这里就能体现定义一个空命令的好处了,如果第一次按撤销命令,那么应该什么都不做;// 如果没有定义空命令的话,此时就需要判断空处理了
command.undo();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker(2);
invoker.setCommand(0, new LightOnCommand(), new LightOffCommand());
invoker.setCommand(1, new TvOnCommand(), new TvOffCommand());
System.out.println("电灯打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(0));
invoker.invoke(invoker.getOffCommand(0));
// invoker.undo();
System.out.println("电视打开关闭操作===========");
invoker.invoke(invoker.getOnCommand(1));
invoker.undo();
}
}
-
注意事项和细节:将发起请求的对象与执行请求的对象解耦 容易实现对请求的撤销和重做 空命令也是一种设计模式,它为我们省去了判空的操作
命令模式不足: 可能导致某些系统有过多的具体命令类,增加了系统的复杂度
与外观模式相似:都是将多个功能聚合在一起 外观模式更多适用于维护;命令模式更多应用于设计;
-
应用:Spring框架中的JdbcTemplate类 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令 界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制
3.备忘录模式(Memento)
定义: 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式
UML类图
案例在这!!
需求:游戏人物大战后状态恢复
-
角色
originator 待保存状态的对象
HeroMemento 备忘录对象
Caretaker 存放备忘录对象的容器;可以是List、Map、或者单个Memento对象 可以保存多个 originator 对象的不同时间的状态
情况1:为一个对象保留一个状态
package com.zking.memento.demo1;
/**
* @author 钟羽
* @site www.javaxl.com * @company
*/
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
}
// 恢复上一个英雄状态
public void getMemento(HeroMemento heroMemento) {
this.state = heroMemento.getState();
}
}
package com.zking.memento.demo1;
public class Caretaker {
private HeroMemento heroMemento;
public HeroMemento getHeroMemento() {
return heroMemento;
}
public void setHeroMemento(HeroMemento heroMemento) {
this.heroMemento = heroMemento;
}
}
package com.zking.memento.demo1;
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
package com.zking.memento.demo1;
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态3:残血状态");
hero.getMemento(caretaker.getHeroMemento());
System.out.println("当前的状态===============" + hero.getState());
caretaker.setHeroMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.setHeroMemento(hero.saveHero());
}
}
情况2:为一个对象保留多个状态
package com.zking.memento.demo2;
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
public void getMemento(Caretaker caretaker, int no) {
this.state = caretaker.getMemento(no).getState();
}
}
package com.zking.memento.demo2;
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
package com.zking.memento.demo2;
import java.util.ArrayList;
import java.util.List;
public class Caretaker {
private List<HeroMemento> heroMementos = new ArrayList<>();
public void addMemento(HeroMemento memento) {
heroMementos.add(memento);
}
public HeroMemento getMemento(int no) {
return heroMementos.get(no);
}
}
package com.zking.memento.demo2;
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
caretaker.addMemento(hero.saveHero());
hero.setState("状态2:状态下滑");
hero.setState("状态3:残血状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态4:临死状态");
caretaker.addMemento(hero.saveHero());
hero.setState("状态5:死亡状态");
// 上面备份了1、3、4状态,我来恢复看看
System.out.println("当前的状态===============" + hero.getState());
hero.getMemento(caretaker, 0);
System.out.println("回复到状态1===============" + hero.getState());
hero.getMemento(caretaker, 1);
System.out.println("回复到状态3===============" + hero.getState());
hero.getMemento(caretaker, 2);
System.out.println("回复到状态4===============" + hero.getState());
}
}
情况3:为多个对象保留一个状态
package com.zking.memento.demo2;
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
}
package com.zking.memento.demo2;
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
package com.zking.memento.demo2;
import java.util.HashMap;
public class Caretaker {
//为多个对象保留一个状态
private HashMap<Caretaker ,HeroMemento> mementos = new HashMap();
public void addMemento(Caretaker caretaker,HeroMemento memento) {
mementos.put(caretaker,memento);
}
public HashMap<Caretaker ,HeroMemento> getMemento() {
return this.mementos;
}
}
package com.zking.memento.demo2;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Client {
public static void main(String[] args) {
Hero hero = new Hero("状态1,满血状态");
Caretaker caretaker = new Caretaker();
Caretaker caretaker2 = new Caretaker();
HeroMemento heroMemento = new HeroMemento(hero.getState());
caretaker.addMemento(caretaker,heroMemento);
caretaker.addMemento(caretaker2,heroMemento);
HashMap<Caretaker, HeroMemento> memento = caretaker.getMemento();
Set<Map.Entry<Caretaker, HeroMemento>> entrySet = memento.entrySet();
for (Map.Entry<Caretaker, HeroMemento> entry : entrySet) {
String key = entry.getKey().toString();
String value = entry.getValue().toString();
System.out.println(key);
System.out.println(value);
}
}
}
情况4:为多个对象保留多个状态
public class Hero {
// 需要存档的属性:这里用一个state属性来表示,实际需要存档的属性可能会有很多
private String state;
public Hero(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
} // 将当前Hero对象实例进行备份
public HeroMemento saveHero() {
return new HeroMemento(this.state);
} // 恢复某一个英雄状态
}
public class HeroMemento {
private String state;
public HeroMemento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class Caretaker {
private HashMap<Caretaker , List<HeroMemento>> mementos = new HashMap();
public void addMemento(Caretaker caretaker,List<HeroMemento> memento) {
mementos.put(caretaker,memento);
}
public HashMap<Caretaker , List<HeroMemento>> getMemento() {
return this.mementos;
}
}
public class Client {
public static void main(String[] args) {
Hero hero1 = new Hero("状态1,满血状态");
Hero hero2 = new Hero("状态2,残血状态");
Hero hero3 = new Hero("状态3,濒死状态");
Hero hero4 = new Hero("状态4,死亡状态");
Caretaker caretaker = new Caretaker();
Caretaker caretaker2 = new Caretaker();
HeroMemento heroMemento1 = new HeroMemento(hero1.getState());
HeroMemento heroMemento2 = new HeroMemento(hero2.getState());
HeroMemento heroMemento3 = new HeroMemento(hero3.getState());
HeroMemento heroMemento4 = new HeroMemento(hero4.getState());
List<HeroMemento> lsit1=new ArrayList<>();
lsit1.add(heroMemento1);
lsit1.add(heroMemento2);
List<HeroMemento> lsit2=new ArrayList<>();
lsit2.add(heroMemento3);
lsit2.add(heroMemento4);
caretaker.addMemento(caretaker,lsit1);
caretaker.addMemento(caretaker2,lsit2);
HashMap<Caretaker, List<HeroMemento>> memento = caretaker.getMemento();
Set<Map.Entry<Caretaker, List<HeroMemento>>> entries = memento.entrySet();
for (Map.Entry<Caretaker, List<HeroMemento>> entry : entries) {
String key = entry.getKey().toString();
String value = entry.getValue().toString();
System.out.println("key=" + key + " value=" + value);
}
}
优点
-
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
-
实现了信息的封装,使得用户不需要关心状态的保存细节
注意:如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
应用
- 事务回滚
- 游戏的存档
- Selenium动态解析资源树
4.状态模式(State)
定义: 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。简单理解,一个拥有状态的context对象,在不同的状态下,其行为会发生改变。
UML类图
案例!!!
抽奖活动项目设计
出现前
package com.javaxl.design.state.before;
import java.util.Random;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class State {
// 当前的状态
private int state;
// 供抽奖的积分
private int score;
// 奖品的数量
private int count;
public State(int score, int count) {
this.score = score;
this.count = count;
}
public int getCount() {
return count;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
// 扣除积分
public void minus() {
// 只有一阶段可以扣积分
this.state = 1;
if (this.state == 1) {
if (this.score >= 50) {
if (this.count == 0) {
System.out.println("奖品领完....");
return;
}
this.score = this.score - 50;
System.out.println("========扣除50积分,当前积分还剩" + this.score + "========");
this.state = 2;
if (luckHit()) {
this.state = 3;
getPrize();
}
} else {
System.out.println("========积分不够,当前积分为" + this.score + "========");
}
}
}
// 十分之一抽中奖品的概率
public boolean luckHit() {
// 只有二阶段可以抽奖
return this.state == 2 ? (new Random().nextInt(10) == 6) : false;
}
public void getPrize() {
if (this.state == 3) {
if (this.count > 0) {
System.out.println("领取奖品....");
this.count = this.count - 1;
} else {
System.out.println("奖品领完....");
}
}
}
}
public class Client {
public static void main(String[] args) {
State state = new State(500,1);
// state.minus();
for (int i = 0; i < 300; i++) {
state.minus();
}
}
}
从上面的编码中,我们可以看出,完成该需求有很多的条件判断,非常不利于后续的维护;上面状态只有4个,代码已经比较复杂了;状态越多,代码嵌套就越复杂,维护成本就越高;
出现后
package com.javaxl.design.state.after;
import java.util.Random;
/**
* @author钟羽
* @site www.javaxl.com
* @company
*/
public abstract class State {
// 扣积分
abstract void minus();
// 抽奖
abstract boolean luckHit();
// 获取奖品
abstract void getPrize();
}
class ConcreteStateA extends State{
Context context;
public ConcreteStateA(Context context) {
this.context = context;
}
@Override
void minus() {
if(context.getScore()>=50){
context.setScore(context.getScore()-50);
System.out.println("========扣除50积分,当前积分还剩"+context.getScore()+"========");
context.setState(context.getStateB());
}else{
System.out.println("========积分不够,当前积分为"+context.getScore()+"========");
}
}
@Override
boolean luckHit() {
System.out.println("还在扣费环节,不能抽奖...");
return false;
}
@Override
void getPrize() {
System.out.println("还在扣费环节,不能领取奖品...");
}
}
class ConcreteStateB extends State{
Context context;
public ConcreteStateB(Context context) {
this.context = context;
}
@Override
void minus() {
System.out.println("已经在抽奖环节...");
}
@Override
boolean luckHit() {
boolean flag = new Random().nextInt(10) == 6;
if(flag){
context.setState(context.getStateC());
}else{
context.setState(context.getStateA());
}
return flag;
}
@Override
void getPrize() {
System.out.println("还在抽奖环节,不能领取奖品...");
}
}
class ConcreteStateC extends State{
Context context;
public ConcreteStateC(Context context) {
this.context = context;
}
@Override
void minus() {
System.out.println("已经在领取奖品环节...");
}
@Override
boolean luckHit() {
System.out.println("已经在领取奖品环节...");
return false;
}
@Override
void getPrize() {
if(context.getCount()>0){
System.out.println("领取奖品成功...");
context.setState(context.getStateA());
}else {
System.out.println("活动结束,领取奖品失败...");
一次没抽中
抽中一次
抽中两次
优点:
- 封装了转换规则。
- 枚举可能的状态,在枚举状态之前需要确定状态种类。
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
应用
借贷平台状态管理
5.职责链模式(Chain of Responsibility)
定义:如果有多个对象有机会处理请求,责任链可使请求的发送者和接受者解耦,请求沿着责任链传递,直到有一个对象处理了它为止
案例!!!!!!
需求:OA系统请假审批案例
学生请假1天:教员审批
学生请假2天:教学主管审批
学生请假3天:教学经理审批
学生请假5天:副校长审批
学生请假超过5天:校长审批
角色
Handler 抽象的处理者, 定义了一个处理请求的接口
ConcreteHandlerA , B 具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者)
Request 含有很多属性,表示一个请求
使用前
package com.javaxl.design.chain.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Request {
private String content;
private int day;
public Request(String content, int day) {
this.content = content;
this.day = day;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
package com.javaxl.design.chain.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Handler {
public void handle(Request request){
int day = request.getDay();
if(day <= 1){
System.out.println("教员处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 2){
System.out.println("教学主管处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 3){
System.out.println("教学经理处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else if(day <= 5){
System.out.println("副校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}else {
System.out.println("校长处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}
}
}
public class Client {
public static void main(String[] args) {
Handler handler = new Handler();
Request request1 = new Request("小感冒",1);
handler.handle(request1);
Request request2 = new Request("做检查",2);
handler.handle(request2);
Request request3 = new Request("打点滴",3);
handler.handle(request3);
Request request4 = new Request("住院",4);
handler.handle(request4);
Request request5 = new Request("在家调养",30);
handler.handle(request5);
}
}
违背了迪米特法则,调用方清楚的知道整个处理链的存在;
使用后
public class Request {
private String content;
private int day;
public Request(String content, int day) {
this.content = content;
this.day = day;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
public abstract class Handler {
Handler next;
String name;
public Handler(String name) {
this.name = name;
}
public Handler getNext() {
return next;
}
public void setNext(Handler next) {
this.next = next;
}
public abstract void handle(Request request);
}
class HandlerA extends Handler {
public HandlerA(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 1) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerB extends Handler {
public HandlerB(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 2) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerC extends Handler {
public HandlerC(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 3) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerD extends Handler {
public HandlerD(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
if (day <= 5) {
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
} else {
next.handle(request);
}
}
}
class HandlerE extends Handler {
public HandlerE(String name) {
super(name);
}
public void handle(Request request) {
int day = request.getDay();
System.out.println(this.name + "处理了:因 " + request.getContent() + " 请假" + day + "天的请求");
}
}
public class Client {
public static void main(String[] args) {
HandlerA handlerA = new HandlerA("教员");
HandlerB handlerB = new HandlerB("教学主管");
HandlerC handlerC = new HandlerC("教学经理");
HandlerD handlerD = new HandlerD("副校长");
HandlerE handlerE = new HandlerE("校长");
handlerA.setNext(handlerB);
handlerB.setNext(handlerC);
handlerC.setNext(handlerD);
handlerD.setNext(handlerE);
Request request1 = new Request("小感冒",1);
handlerA.handle(request1);
Request request2 = new Request("做检查",2);
handlerA.handle(request2);
Request request3 = new Request("打点滴",3);
handlerA.handle(request3);
Request request4 = new Request("住院",4);
handlerA.handle(request4);
Request request5 = new Request("在家调养",30);
handlerA.handle(request5);
}
}
注意事项和细节
将请求和处理分开,实现解耦,提高系统的灵活性简化了对象,使对象不需要知道链的结构
注意:性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
应用
springmvc框架~HandlerExcutionChain类
工作流框架绘图生成xml反射实例化,整个流程归档过程
js中的原型链
6.观察者模式(Observer)
定义: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
UML类图
案例来了!!!!
需求:气象站数据更新推送问题
出现前
package com.javaxl.design.observer.before;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class WeatherData {
double temperature;
double humidity;
public WeatherData(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
public void getWeatherInfo() {
System.out.println("当前温度:" + temperature + ",当前湿度:" + humidity);
}
public void change(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
}
public class Baidu {
private WeatherData weatherData;
public Baidu(WeatherData weatherData) {
this.weatherData = weatherData;
}
public void getWeatherInfo() {
System.out.print("百度网站温馨提示===>");
weatherData.getWeatherInfo();
}
}
class Sina {
private WeatherData weatherData;
public Sina(WeatherData weatherData) {
this.weatherData = weatherData;
}
public void getWeatherInfo() {
System.out.print("新浪网站温馨提示===>");
weatherData.getWeatherInfo();
}
}
public class Client {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(30,20);
Baidu baidu = new Baidu(weatherData);
Sina sina = new Sina(weatherData);
baidu.getWeatherInfo();
sina.getWeatherInfo();
weatherData.change(10,10);
baidu.getWeatherInfo();
sina.getWeatherInfo();
}
}
由第三方(百度、新浪)主动获取最新天气信息,这种方案需要每个第三方主动定时获取最新天气数据,涉及多个第三方;
出现后
package com.javaxl.design.observer.after;
import java.util.ArrayList;
import java.util.List;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
class WeatherData implements Subject{
double temperature;
double humidity;
List<Observer> Observers = new ArrayList<>();
public WeatherData(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
}
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
// 气象局数据一改变,马上通知接入的第三方/观察者
notifyObservers();
}
@Override
public void addObserver(Observer observer) {
Observers.add(observer);
observer.update(this.temperature,this.humidity);
}
@Override
public void removeObserver(Observer observer) {
Observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : Observers) {
observer.update(this.temperature,this.humidity);
}
}
}
public interface Observer {
void display();
void update(double temperature, double humidity);
}
class Baidu implements Observer{
double temperature;
double humidity;
@Override
public void display() {
System.out.println("百度温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
}
@Override
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
this.display();
}
}
class Sina implements Observer{
double temperature;
double humidity;
@Override
public void display() {
System.out.println("新浪温馨提示:当前温度:" + temperature + ",当前湿度:" + humidity);
}
@Override
public void update(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
this.display();
}
}
public class Client {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(30, 20);
Baidu baidu = new Baidu();
Sina sina = new Sina();
weatherData.addObserver(baidu);
weatherData.addObserver(sina);
weatherData.update(10, 10);
weatherData.removeObserver(baidu);
weatherData.update(12, 12);
}
}
由气象局主动通知第三方,天气数据发生了改变;并且,第三方的接入可以控制(增加、删除、通知);
-
注意事项和细节
集合的方式来管理用户(Observer),包括注册,移除和通知
-
应用
JDK源码中Observable类
7.策略模式(Strategy)
定义: 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户
案例来了!!!!!
需求:学院共有专业需求
出现前
package com.javaxl.design.strategy.before;
/**
* @author钟羽
* @site www.javaxl.com
* @company
*/
public class Major {
private String name;
public Major(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Major major = (Major) obj;
return this.name.equals(major.name);
}
}
public class College {
String name;
public College(String name) {
this.name = name;
}
}
class CollegeA extends College{
List<Major> list = new ArrayList<>();
CollegeA(String name){
super(name);
this.list.add(new Major("JAVA"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("android"));
}
}
class CollegeB extends College{
List<Major> list = new ArrayList<>();
CollegeB(String name){
super(name);
this.list.add(new Major("iOS"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("嵌入式"));
}
}
public class StrategyA {
public List<Major> intersect(List<Major> a,List<Major> b){
List<Major> list = new ArrayList();
for (Major major : a) {
if(b.contains(major)){
list.add(major);
}
}
return list;
}
}
class StrategyB {
public List<Major> intersect(List<Major> a,List<Major> b){
// a.retainAll(b);
b.retainAll(a);
return b;
}
}
public class Client {
public static void main(String[] args) {
StrategyA strategyA = new StrategyA();
CollegeA a = new CollegeA("华东交通大学");
CollegeB b = new CollegeB("东华理工大学");
List<Major> intersect = strategyA.intersect(a.list, b.list);
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect) {
System.out.println(major.getName());
}
StrategyB strategyB = new StrategyB();
List<Major> intersect2 = strategyB.intersect(a.list, b.list);
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect2) {
System.out.println(major.getName());
}
}
}
出现后
package com.javaxl.design.strategy.after;
/**
* @author 钟羽
* @site www.javaxl.com
* @company
*/
public class Major {
private String name;
public Major(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Major major = (Major) obj;
return this.name.equals(major.name);
}
}
public class College {
String name;
public College(String name) {
this.name = name;
}
}
class CollegeA extends College {
List<Major> list = new ArrayList<>();
CollegeA(String name){
super(name);
this.list.add(new Major("JAVA"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("android"));
}
}
class CollegeB extends College {
List<Major> list = new ArrayList<>();
CollegeB(String name){
super(name);
this.list.add(new Major("iOS"));
this.list.add(new Major("PHP"));
this.list.add(new Major("JavaScript"));
this.list.add(new Major("C语言"));
this.list.add(new Major("嵌入式"));
}
}
public interface Strategy {
List<Major> intersect(List<Major> a, List<Major> b);
}
public class StrategyA implements Strategy{
public List<Major> intersect(List<Major> a,List<Major> b){
List<Major> list = new ArrayList();
for (Major major : a) {
if(b.contains(major)){
list.add(major);
}
}
return list;
}
}
public class Context {
public List<Major> intersect(List<Major> a, List<Major> b,Strategy strategy){
return strategy.intersect(a,b);
}
}
public class Client {
public static void main(String[] args) {
CollegeA a = new CollegeA("华东交通大学");
CollegeB b = new CollegeB("东华理工大学");
Context context = new Context();
List<Major> intersect = context.intersect(a.list, b.list, new StrategyA());
System.out.println(a.name + "与" + b.name + "都有的专业");
for (Major major : intersect) {
System.out.println(major.getName());
}
// 可以随意定制策略
List<Major> intersect2 = context.intersect(a.list, b.list, new Strategy() {
@Override
public List<Major> intersect(List<Major> a, List<Major> b) {
a.retainAll(b);
return a;
}
});
System.out.println(a.name + "与" + b.name + "都有的专业========");
for (Major major : intersect2) {
System.out.println(major.getName());
}
}
}
-
注意事项和细节
-
分析项目中变化部分与不变部分
-
体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可
-
策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
-
注意:在很多场景中,策略接口会作为内部接口体现
-
应用
-
Arrays工具类的排序方法Comparator策略接口的使用
-
JDBC对Result结果集的处理
-
作者还在持续更新中,如果有什么写的不对的欢迎大家私信作者
这篇文章中有借鉴其他大佬的笔记23 种设计模式详解(全23种)_鬼灭之刃的博客-优快云博客_设计模式