将设计者的思维融入大家的学习和工作中,更高层次的思考!
创建型模式 :
单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式;
结构型模式 :
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式;
行为型模式 :
模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式,访问者模式;
静态内部类实现方式(也是一种赖加载方式)
public class SingletonDemo04 {
private static class SingletonClassInstance {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonClassInstance.instance;
}
private SingletonDemo04() {
}
}
要点 :
1: 外部类没有static属性, 则不会像饿汉式哪有立即加载对象.
2: 只有真正调用getInstance(), 才会加载静态内部类. 加载类时是线程安全的. instance是static final类型, 保证了内存中只有这样一个实例存在, 而且只能被
赋值一次, 从而保证了线程安全性.
兼备了并发高效调用和延迟加载的优势.
用枚举实现单例模式(没有延迟加载)
public enum SinglettomDemo5 {
// 这个枚举元素, 本身就是单例对象
INSTANCE;
// 添加自己需要的操作
public void singletonOperation() {
}
}
常见的五种单例模式实现方式 :
主要 :
1 : 饿汉式(线程安全, 调用效率高. 但是, 不能延时加载).
2 : 懒汉式(线程安全, 调用效率不高. 但是, 可以延时加载.)
其他 :
1 : 双重检测锁式(由于JVM底层内部模型原因, 偶尔会出现问题. 不建议使用)
2 : 静态内部类式(线程安全, 调用效率高. 但是, 可以延时加载)
3 : 枚举式(线程安全, 调用效率高, 不能延时加载. 并且可以天然的防止反射和反序列化漏洞!)
如何选用?
单例对象 : 占用 资源少, 不需要延时加载 :
枚举式 好于 饿汉式
单例对象 占用 资源大, 需要延时加载 :
静态内部类式 好于饿汉式
package com.example.demo.test;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 测试懒汉式单例模式(如何防止反射和反序列化漏洞)
*/
public class SingletonDemo6 implements Serializable{
// 类初始化时,不初始化这个对象(延时加载, 真正用的时候再创建)
private static SingletonDemo6 instance;
// 私有化构造器
private SingletonDemo6() {
}
// 方法同步,调用效率低!
public static synchronized SingletonDemo6 getInstance() {
if (instance == null) {
return instance = new SingletonDemo6();
}
return instance;
}
// 反序列化时, 如果定义了readResolve()则直接返回此方法指定的对象. 而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
package com.example.demo.test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Client2 {
public static void main(String[] args) throws Exception {
SingletonDemo6 singletonDemo6 = SingletonDemo6.getInstance();
SingletonDemo6 singletonDemo61 = SingletonDemo6.getInstance();
System.out.println(singletonDemo6);
System.out.println(singletonDemo61);
// 通过反射的方式直接调用私有构造器
/*Class<SingletonDemo6> clazz = SingletonDemo6.class;
Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
SingletonDemo6 s3 = c.newInstance();
SingletonDemo6 s4 = c.newInstance();
System.out.println(s3);
System.out.println(s4);*/
// 通过反序列化的方式构造多个对象
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singletonDemo6);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletonDemo6 s3 = (SingletonDemo6) ois.readObject();
System.out.println(s3);
}
}
package com.example.demo.test;
import java.util.concurrent.CountDownLatch;
public class Client3 {
public static void main(String[] srgs) throws Exception {
long start = System.currentTimeMillis();
int count = 10;
final CountDownLatch countDownLatch = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
Object o = SingletonDemo6.getInstance();
}
countDownLatch.countDown();
}
}).start();
}
// main线程阻塞, 指定计数器为0,才会继续往下执行!
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("总耗时 : " + (end - start));
}
}
CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch的作用和Thread.join()方法类似.让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒.
在之间创建线程的年代(java5.0之前), 我们可以使用Thread.join().在线程池出现后,因为线程池中的线程不能直接被引用,所以就必须使用CountDownLatch了.
CountDownLatch主要有两个方法, 当一个或多个线程调用await方法时,这些线程会阻塞.其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行.
实现原理 :
1: 计数器的值由构造函数传入,并用它初始化AQS的state值.当线程调用await方法时会检查state的值是否为0,如果是就直接返回(既不会阻塞); 如果不是, 将表示该节点
的线程入列, 然后将自身阻塞.当其它线程调用countDown方法会将计数器减1,然后判断计数器的值是否为0,当它为0时,会唤醒队列中的第一个节点,由于CountDownLatch使用了
AQS的共享模式,所以第一个节点被唤醒后又会唤醒第二个节点,以此类推,使得所有因await方法阻塞的线程都能被唤醒而继续执行.
从源码和实现原理中可以看出一个CountDownLatch对象,只能使用一次,不能重复使用.
await方法源码 :
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg) throws InterruptedException{
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (tryAcquireShared(arg) < 0) {
doAcquireSharedInterruptibly(arg);
}
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
doAcquireSharedInterruptibly 主要实现线程的入列与阻塞
countDown方法 :
public void countDown() {
sync.releaseShared();
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) {
return false;
}
int nextc = c-1;
if (compareAndSetState(c, nextc)) {
return nextc == 0;
}
}
}
doReleaseShared主要实现唤醒第一个节点, 第一个节点有会唤醒第二个节点,
使用示例 :
public class CountDownLatchDemo {
private CountDownLatch cd1 = new CountDownLatch(2);
private Random rnd = new Random();
class FirstTask implements Runnable {
private String id;
public FirstTask(String id) {
this.id = id;
}
@Override
public void run() {
System.out.println("Thread" + id + "is start");
try {
Thread.sleep(rnd.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread" + id + "is over");
cd1.countDown();
}
}
class SecondTask implements Runnable {
private String id;
public SecondTask(String id) {
this.id = id;
}
@Override
public void run() {
try {
cd1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------Thread" + id + " is start");
try {
Thread.sleep(rnd.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------Thread" + id + " is over")
}
}
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
CountDownLatchDemo cold = new CountDownLatchDemo();
es.submit(cdld.new SecondTask("c"));
es.submit(cdld.new SecondTask("d"));
es.submit(cdld.new FirstTask("a"));
es.submit(cdld.new FirstTask("b"));
es.shutdown();
}
}
工厂模式 :
工厂模式 : 实现了创建者和调用者的分离.
详细分类 :
简单工厂模式,工厂方法模式,抽象工厂模式.
面向对象设计的基本原则 :
OCP(开闭原则,Open-Closed Principle) : 一个软件的实体应当对扩展开放,对修改关闭.
DIP(依赖倒转原则, Dependence Inversion Principle) : 要针对接口编程,不要针对实现编程.实现尽量依赖抽象,不依赖具体实现.
LoD(迪米特法则,Law of Demeter) : 只与你直接的朋友通信,而避免和陌生人通信.又叫最少知识原则,一个软件的实体应尽可能少的与其他实体 发送相互作用 .
单一职责原则 : 每一个类应该专注于做一件事情.
里氏替换原则 : 超类存在的地方,子类是可以替换的.
接口隔离原则 : 应当为客户端提供尽可能小的单独的接口,而不是提供大的总得接口.
组合/聚合复用原则 : 尽量使用合成/聚合达到复用,尽量少用继承. 原则 : 一个类中有另一个类的对象.
工厂模式的核心本质 :
实例化对象,用工厂方法代替new操作.
将选择实现类,创建对象统一管理和控制.从而将调用者跟我们的实现类解耦.
工厂模式 :
简单工厂模式 : 用来生产同一等级结构中的任意产品.(对于增加新的产品,需要修改已有代码)
工厂方法模式 : 用来生产同一等级结构中的固定产品.(支持增加任意产品)
抽象工厂模式 : 用来生产不同产品族的全部产品.(对于增加新的产品,无能为力;支持增加产品族)
简单工厂 :
public class CarFactory {
public static Car createCar(String type) {
if ("奥迪".equals(type)) {
return new Audi();
} else if ("比亚迪".equals(type)) {
return new Byd();
} else {
return null;
}
}
}
public class CarFactory {
public static Car createAudi() {
return new Audi();
}
public static Car createBeans() {
return new Bens();
}
}
工厂方法模式要点 :
为力避免简单工厂模式的缺点,不完全满足OCP.
工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类.
抽象工厂模式 :
用来生产不同产品族的全部产品.(对于增加新的产品,无能为力,支持增加产品族)
抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种,业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式.
工厂模式要点 :
简单工厂模式(静态工厂模式) : 虽然某种程度不符合设计原则,但实际使用最多.
工厂方法模式 : 不修改已有类的前提下,通过增加新的工厂类实现扩展.
抽象工厂模式 : 不可以增加产品,可以增加产品族.
应用场景 :
JDK中Calendar的getInstance方法;
JDBC中Connection对象的获取;
Hibernate中SessionFactory创建Session;
spring中IOC容器创建管理bean对象;
XML解析时的DocumentBuilderFactory创建解析器对象;
反射中Class对象的newInstance();
建造者模式 :
在实际开发中,我们所需要的对象构建时,也非常复杂,有很多步骤需要处理时.
建造模式的本质 :
分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责).从而可以构造出复杂的对象.这个模式适用于 : 某个对象的构建过程复杂的情况下使用.
由于实现了构建和装配的解耦.不同的构建器,相同的装配,也可以做出不同的对象.也就是实现了构建算法,装配算法的解耦,实现了更好的复用.
开发中应用场景 :
例如StringBuilder : 利用了构建者模式;还有XML解析中,JDOM库中的类 : DomBuilder,SaxBuilder也是利用了构建者模式;SQL中的PreparedStatement
原型模式prototype :
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式.
就是java中的克隆技术,以某个对象为原型,复制出新的对象.显然,新的对象具备原型对象的特定;
优势有 : 效率高(直接克隆,避免了重新执行构造过程步骤)
克隆类似于new,但是不同于new.new创建新的对象属性采用的是默认值.克隆出的对象的属性值完全和原型对象相同.并且克隆出的新对象改变不会影响原型对象.然后,
再修改克隆对象的值.
原型模式实现 :
Cloneable接口和clone方法
Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了clone()方法替我们做了绝大部分事情.
注意用词 : 克隆和拷贝一回事.
例子 :
public class Sheep implements Cloneable {
private String sname;
private Date birthday;
public Sheep(String sname, Date date) {
this.sname = sname;
this.birthday = date;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 直接调用object对象的clone方法
Object clone = super.clone();
//添加如下代码实现深复制(deep Clone)
Sheep s = (Sheep)clone;
// 把属性也进行克隆(浅克隆对于对象的克隆只是克隆了引用地址,当引用地址指向的原地址中值改变了,克隆以后的值也改变了.而深克隆是专门把原有的再复制出来一份)
s.birthday = (Date) this.birthday.clone();
return clone;
}
利用序列化和反序列化技术实现深克隆 :
开发中的应用场景 : 原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者.
spring中bean的创建实际就是两种 : 单例模式和原型模式.(当然,原型模式需要和工厂模式搭配起来使用)
例子 :
Date date = new Date();
Sheep s1 = new Sheep("少理",date);
System.out.println(s1);
System.out.println(s1.toString());
// 使用序列化和反序列化实现深复制
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s1);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
// 克隆好的对象
Sheep s2 = (Sheep) ois.readObject();
System.out.println("修改克隆好的对象属性值");
s1.setBirthday(new Date(18239139371319l));
System.out.println("----------------------");
// Sheep clone = (Sheep)s1.clone();
System.out.println(s2);
System.out.println(s2.toString());
注意 : 如果需要短时间创建大量对象,并且new的过程比较耗时.则可以考虑使用原型模式.
总结 :
创建型模式 : 都是用来帮助我们创建对象的.
单例模式 : 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点.
工厂模式 :
简单工厂模式 : 用来生产同一等级结构中的任意产品.(对于增加新的产品,需要修改已有代码)
工厂方法模式 : 用来生产同一等级结构中的固定产品.(支持增加任意产品)
抽象工厂模式 : 用来生产不同产品族的全部产品.(对于增加新的产品,无能为力;支持增加产品族)
建造者模式 :
分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责).从而可以构造出复杂的对象.
原型模式 :
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式.
结构型模式 :
核心作用 : 是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题.
分类 :
适配器模式,代理模式,桥接模式,装饰模式,组合模式,外观模式,享元模式.
什么是适配器模式?
将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作.
模式中的角色 :
目标接口(Target) : 客户所期待的接口.目标可以是具体的或抽象的类,也可以是接口.
需要适配的类(Adaptee) : 需要适配的类或适配者类.
适配器(Adapter) : 通过包装一个需要适配的对象,把原接口转换成目标接口.
工作中的场景 :
经常用来做旧系统改造和升级;
如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍.
我们常见的场景 :
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
例子 :
package com.example.demo.adapter;
/**
* 被适配的类
* (相当于例子中的,PS/2键盘)
*/
public class Adaptee {
public void request() {
System.out.println("可以完成客户请求的功能!");
}
}
package com.example.demo.adapter;
/**
* 适配器(相当于usb和ps/2的转接器)(类适配器方式)
*/
public class Adapter extends Adaptee implements Target {
@Override
public void handleReq() {
super.request();
}
}
package com.example.demo.adapter;
/**
* 适配器(对象适配器方式,使用了组合的方式跟被适配对象整合)
* (相当于usb和ps/2的转接器)
*/
public class Adapter2 implements Target {
private Adaptee adaptee;
@Override
public void handleReq() {
adaptee.request();
}
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
}
package com.example.demo.adapter;
public interface Target {
void handleReq();
}
package com.example.demo.adapter;
/**
* 客户的类
* (相当于例子中的笔记本,只有USB接口)
*/
public class Client {
public void test1(Target t) {
t.handleReq();
}
public static void main(String[] args) {
/* Client c = new Client();
//Adaptee a = new Adaptee();
Target t = new Adapter();
c.test1(t);*/
Client c = new Client();
Adaptee adaptee = new Adaptee();
Target target = new Adapter2(adaptee);
c.test1(target);
}
}
代理模式(Proxy pattern) :
核心作用 :
通过代理,控制对对象的访问!
可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理.(既 : AOP的微观实现!)
AOP(面向切面编程)的核心实现机制!
核心角色 :
抽象角色 : 定义代理角色和真实角色的公共对外方法;
真实角色 :
实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用.关注真正的业务逻辑!
代理角色 :
实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作.
将统一的流程控制放到代理角色中处理!
应用场景 :
安全代理 : 屏蔽对真实角色的直接访问.
远程代理 : 通过代理类处理远程方法调用(RMI)
延迟加载 : 先加载轻量级的代理对象,真正需要再加载真实对象.
(比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打卡文件时不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要
查看图片时,用proxy来进行大图片的打卡.)
分类 :
静态代理(静态定义代理类)
动态代理(动态生成代类)
JDK自带的动态代理;
javaassist字节码操作库实现
CGLIB
ASM(底层使用指令,可维护性较差)
动态代理的优点 :
动态代理相比于静态代理的优点 :
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法.
JDK自带的实现 :
java.lang.reflect.Proxy : 作用 : 动态生成代理类和对象.
java.lang.reflect.InvocationHandler(处理器接口) :
可以通过invoke方法实现对真实角色的代理访问.
每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象.
例子 :
Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Star.class},handler);
proxy.sing();
开发框架中应用场景 :
struts2中拦截器的实现;
数据库连接处关闭处理;
Hibernate中延迟加载的实现;
mybatis中实现拦截器插件;
AspectJ的实现;
spring中AOP的实现 : 日志拦截,声明式事务处理;
web service;
RMI远程方法调用;
桥接模式(bridge) :
场景 : 商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题.
用来处理多层继承结构;
问题 :
扩展性问题(类个数膨胀问题) :
如果要增加一个新的电脑类型智能手机,则要增加各个品牌下面的类.
如果要增加一个新的品牌,也要增加各种电脑类型的类.
违反单一职责原则 :
一个类 : 联想笔记本,有两个引起这个类变化的原因.
桥接模式核心要点 :
处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联.
桥接模式总结 :
桥接模式可以取代多层继承的方案.多层继承违背了单一职责原则,复用性较差,类的个数也非常多.桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本.
桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则.
就像一个桥,将两个变化维度连接起来,各个维度都可以独立的变化.故称之为 : 桥模式.(只要具备多个编号维度的时候,就可以使用桥接模式)
桥接模式实际开发中应用场景 :
JDBC驱动程序;
AWT中的Peer架构;
银行日志管理;
格式分类 : 操作日志,交易日志,异常日志;
距离分类 : 本地记录日志,异地记录日志;
人力资源系统中的奖金计算模块 :
奖金分类 : 个人奖金,团体奖金,激励奖金.
部门分类 : 人事部门,销售部门,研发部门.
OA系统中的消息处理 :
业务类型 : 普通消息,加急消息,特急消息;
发送消息方式 : 系统内消息,手机短信,邮件;
组合模式(composite)
使用组合模式的场景 :
把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象.
组合模式核心 :
抽象构件(Component)角色 : 定义了叶子和容器构件的共同点;
叶子(Leaf)构件角色 : 无子节点;
容器(Composite)构件角色 : 有容器特征,可以包含子节点.
组合模式工作流程分析 :
组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性的对待容器和叶子.
当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行.其中,使用了递归调用的机制对整个结构进行处理.
使用组合模式,模拟杀毒软件架构设计.
开发中的应用场景 :
操作系统的资源管理器;
GUI中的容器层次图;
XML文件解析;
OA系统中,组织结构的处理;
Junit单元测试框架 : 底层设计就是典型的组合模式,TestCase(叶子),TestUnit(容器),Test接口(抽象)
例子 :
package com.example.demo.composite;
import java.util.ArrayList;
import java.util.List;
// 抽象构建
public interface AbstractFile {
// 杀毒
void killVirus();
}
class ImageFile implements AbstractFile {
private String name;
public ImageFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("--图像文件 : " + name + ",进行查杀!");
}
}
class TextFile implements AbstractFile {
private String name;
public TextFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---文本文件 : " + name + ",进行查杀!");
}
}
class VideoFile implements AbstractFile {
private String name;
public VideoFile(String name) {
this.name = name;
}
@Override
public void killVirus() {
System.out.println("---视频文件 : " + name + ",进行查杀!");
}
}
class Folder implements AbstractFile {
private String name;
// 定义容器,用来存放本容器构建下的子节点
private List<AbstractFile> list = new ArrayList<AbstractFile>();
public Folder(String name) {
this.name = name;
}
public void add(AbstractFile file) {
list.add(file);
}
public void remove(AbstractFile file) {
list.remove(file);
}
public AbstractFile getChild(int index) {
return list.get(index);
}
@Override
public void killVirus() {
System.out.println("---文件夹 : " + name + ",进行查杀!");
for (AbstractFile file : list) {
file.killVirus();
}
}
}
package com.example.demo.composite;
public class Clinet {
public static void main(String[] args) {
AbstractFile f2, f3, f4, f5, f6;
Folder f1 = new Folder("我的收藏");
f2 = new ImageFile("老高的大头像.jpg");
f3 = new TextFile("Hello.txt");
f1.add(f2);
f1.add(f3);
Folder f11 = new Folder("电影");
f4 = new VideoFile("笑傲江湖.avi");
f5 = new VideoFile("神雕侠侣.avi");
f11.add(f4);
f11.add(f5);
f1.add(f11);
//f2.killVirus();
f1.killVirus();
}
}
装饰模式(decorator)
职责 :
动态的为一个对象增加新的功能.
装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能.使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀.
实现细节 :
Component抽象构建角色 : 真实对象和装饰对象有相同的接口.这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互.
ConcreteComponent具体构建角色(真实对象) : io流中的FileInputStream,FileOutputStream
Decorator装饰角色 : 持有一个抽象构建的引用.装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象.这样,就能在真实对象调用前后增加新的功能.
ConcreteDecorator具体装饰角色 : 负责给构建对象增加新的责任.
开发中使用的场景 :
IO中输入流和输出流的设计;
Swing包中图形界面构件功能;
Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能.
Struts2中,request,response,session对象的处理.
IO流实现细节 :
Component抽象构件角色 : io流中的InputStream,OutputStream,Reader,Writer
ConcreteComponent具体构件角色 : io流中的FileInputStream,FileOutputStream
Decorator装饰角色 : 持有一个抽象构件的引用 : io流中的FilterInputStream,FilterOutputStream
COncreteDecorato具体装饰角色 : 负责给构建对象增加新的责任.Io流中的BufferedOutputStream,BufferedInputStream等.
总结 :
装饰模式(Decorator)也叫包装器模式(Wrapper)
装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类.
优点 :
扩展对象功能,比继承灵活,不会导致类个数急剧增加.
可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象.
具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构建子类和具体装饰子类.
缺点 :
产生很多小对象.大量小对象占据内存,一定程度上影响性能.
装饰模式易于出错,调试排查比较麻烦.
装饰模式和桥接模式的区别 :
两个模式都是为了解决过多子类对象问题.但他们诱因不一样.桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定.装饰模式是为了增加新的功能.
例子 :
package com.example.demo.decorator;
/**
* 抽象构建
*/
public interface ICar {
void move();
}
// ConcreteComponent 具体构件角色(真实对象)
class Car implements ICar {
@Override
public void move() {
System.out.println("陆地上跑!");
}
}
// Decorator装饰角色
class SuperCar implements ICar {
protected ICar car;
public SuperCar(ICar car) {
this.car = car;
}
@Override
public void move() {
car.move();
}
}
// ConcreteDecorator具体装饰角色
class FlyCar extends SuperCar {
public FlyCar(ICar car) {
super(car);
}
public void fly() {
System.out.println("天上飞!");
}
@Override
public void move() {
super.move();
fly();
}
}
// ConcreteDecorator具体装饰角色
class WaterCar extends SuperCar {
public WaterCar(ICar car) {
super(car);
}
public void swim() {
System.out.println("水上飞!");
}
@Override
public void move() {
super.move();
swim();
}
}
// ConcreteDecorator具体装饰角色
class AICar extends SuperCar {
public AICar(ICar car) {
super(car);
}
public void autoMove() {
System.out.println("自动跑的机器人!");
}
@Override
public void move() {
super.move();
autoMove();
}
}
package com.example.demo.decorator;
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("增加新的功能,飞行 -----");
FlyCar car1 = new FlyCar(car);
car1.move();
System.out.println("增加新的功能,水里游-----");
WaterCar waterCar = new WaterCar(car);
waterCar.move();
System.out.println("增加两个新的功能,飞行,水里游-------");
WaterCar waterCar1 = new WaterCar(new FlyCar(car));
waterCar1.move();
}
}
外观模式 :
迪米特法则(最少知识原则) : 一个软件实体应当尽可能少的与其他实体发送相互作用.
外观模式核心 : 为子系统提高统一的入口,封装子系统的复杂性,便于客户端调用.
开发中常见的场景 :
频率很高.哪里都会遇到.各种技术和框架中,都有外观模式的使用.如 : JDBC封装后的,commons提供的DBUtils类,Hibernate提供的工具类,Spring JDBC工具类等.
享元模式(FlyWeight) :
场景 : 内存属于稀缺资源,不要随便浪费.如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存.
核心 : 享元模式以共享的方式高效的支持大量细粒度对象的重用.
享元模式能做到共享的关键和区分了内部状态和外部状态.
内部状态 : 可以共享,不会随环境变化而改变.
外部状态 : 不可以共享,会随环境变化而改变.
享元模式实现 :
FlyweightFactory享元工厂类 : 创建并管理享元对象,享元池一般设计成键值对.
FlyWeight抽象享元类 : 通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态.
ConcreteFlyWeight具体享元类 : 为内部状态提供成员变量进行存储.
UnsharedConcreteFlyWeight非共享享元类 : 不能被共享的子类可以设计为非共享享元类.
享元模式开发中应用的场景 :
享元模式由于其共享的特性,可以再任何'池'中操作,比如 : 线程池,数据库连接池.String类的设计也是享元模式.
优点 :
极大减少内存中对象的数量;
相同或相似对象内存中只存一份,极大的节约资源,提供系统性能;
外部状态相对独立,不影响内部状态.
缺点 :
模式较复杂,使程序逻辑复杂化;
为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长.用时间换取了空间.
例子 :
package com.example.demo.flyweight;
/**
* 享元类
*/
public interface ChessFlyWeight {
void setColor(String c);
String getColor();
void display(Coordinate c);
}
class ConcreteChess implements ChessFlyWeight {
private String color;
public ConcreteChess(String color) {
this.color = color;
}
@Override
public void setColor(String c) {
this.color = c;
}
@Override
public String getColor() {
return color;
}
@Override
public void display(Coordinate coordinate) {
System.out.println("棋子颜色 : " + color);
System.out.println("棋子位置 : " + coordinate.getX() + "-----" + coordinate.getY());
}
}
package com.example.demo.flyweight;
import java.util.HashMap;
import java.util.Map;
/**
* 享元工厂类
*/
public class ChessFlyWeightFactory {
// 享元池
private static Map<String, ChessFlyWeight> map = new HashMap<>();
public static ChessFlyWeight getChess(String color) {
if (map.get(color) != null) {
return map.get(color);
} else {
ChessFlyWeight cfw = new ConcreteChess(color);
map.put(color, cfw);
return cfw;
}
}
}
package com.example.demo.flyweight;
/**
* 外部状态UnSharedConcreteFlyWeight
*/
public class Coordinate {
private int x,y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package com.example.demo.flyweight;
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("增加外部状态的处理======");
chess1.display(new Coordinate(10, 10));
chess2.display(new Coordinate(20, 20));
}
}
结构型模式汇总 :
代理模式 : 为真实对象提供一个代理,从而控制对真实对象的访问;
适配模式 : 使原本由于接口不兼容不能一起工作的类可以一起工作;
桥接模式 : 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联.
组合模式 : 将对象组合成树状结构以表示"部分和整体"层次结构,使得客户可以统一的调用叶子对象和容器对象.
装饰模式 : 动态的给一个对象添加额外的功能,比继承灵活.
外观模式 : 为子系统提供统一的调用接口,使得子系统更加容易使用.
享元模式 : 运用共享技术有效的实现管理大量细粒度对象,节省内存,提供效率.
创建型模式关注对象的创建过程.
结构型模式关注对象和类的组织.
行为型模式 :
关注系统中对象之间的相互交互,研究系统在运行时对象之间 的相互通信和协作,进一步明确对象的职责,共有11种模式.
行为型模式汇总 :
(1) 责任链模式 chain of responsibility
(2) 命令模式 command
(3) 解释器模式 interpreter
(4) 迭代器模式 iterator
(5) 中介者模式 mediator
(6) 备忘录模式 memento
(7) 观察者模式 observer
(8) 状态模式 state
(9) 策略模式 strategy
(10) 模板方法模式 template method
(11) 访问者模式 visitor
责任链模式(chain or responsibility) :
定义 : 将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象.
场景 :
打牌时,轮流出牌;接力赛跑;大学中,奖学金审批;公司中,公文审批;
添加新的处理对象 :
由于责任链的创建完成在客户端,因此新增新的具体处理这对原有类库没有任何影响,只需添加新的类,然后再客户端调用时添加即可.符合开闭原则.
链表方式定义责任链 :
非链表方式实现责任链 : 通过集合,数组生成责任链更加实用!实际上,很多项目中,每个具体的Handler并不是开发团队定义的,而是项目上线后由外部单位追加的,
所以使用链表方式订阅COR链就很困难.
开发中常见的场景 :
Java中,异常机制就是一种责任链模式.一个try可以对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch.
JavaScript语言中,事件的冒泡和捕获机制.Java语言中,事件的处理采用观察者模式.
Servlet开发中,过滤器的链式处理.
Struts2中,拦截器的调用也是典型的责任链模式.
例子中 :
package com.example.demo.chainOfResp;
/**
* 封装请假的基本信息
*/
public class LeaveRequest {
private String empName;
private int leaveDays;
private String reason;
public LeaveRequest(String empName, int leaveDays, String reason) {
super();
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
package com.example.demo.chainOfResp;
/**
* 主任
*/
public class Director extends Leader {
public Director(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if (request.getLeaveDays() < 3) {
System.out.println("员工 : " + request.getEmpName() + "请假,天数 : " + request.getLeaveDays() + ", 理由 : " + request.getReason());
System.out.println("主任 : " + this.name + ", 审批通过!");
} else {
if (this.nextLeader != null) {
this.nextLeader.handleRequest(request);
}
}
}
}
package com.example.demo.chainOfResp;
/**
* 副总
*/
public class ViceGeneralManager extends Leader {
public ViceGeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if (request.getLeaveDays() < 60) {
System.out.println("员工 : " + request.getEmpName() + "请假,天数 : " + request.getLeaveDays() + ", 理由 : " + request.getReason());
System.out.println("总经理 : " + this.name + ", 审批通过!");
} else {
System.out.println("无法申请请假!");
}
}
}
package com.example.demo.chainOfResp;
public class GeneralManager extends Leader {
public GeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if (request.getLeaveDays() < 30) {
System.out.println("员工 : " + request.getEmpName() + "请假,天数 : " + request.getLeaveDays() + ", 理由 : " + request.getReason());
System.out.println("总经理 : " + this.name + ", 审批通过!");
} else {
if (this.nextLeader != null) {
this.nextLeader.handleRequest(request);
}
}
}
}
package com.example.demo.chainOfResp;
/**
* 抽象类
*/
public abstract class Leader {
protected String name;
// 责任链上后继对象
protected Leader nextLeader;
public Leader(String name) {
this.name = name;
}
// 设定责任链上的后继对象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
/**
* 处理请求的核心的业务方法
*/
public abstract void handleRequest(LeaveRequest request);
}
package com.example.demo.chainOfResp;
/**
* 经理
*/
public class Manager extends Leader {
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if (request.getLeaveDays() < 10) {
System.out.println("员工 : " + request.getEmpName() + "请假,天数 : " + request.getLeaveDays() + ", 理由 : " + request.getReason());
System.out.println("经理 : " + this.name + ", 审批通过!");
} else {
if (this.nextLeader != null) {
this.nextLeader.handleRequest(request);
}
}
}
}
package com.example.demo.chainOfResp;
public class Client {
public static void main(String[] args) {
Leader a = new Director("张三");
Leader b = new Manager("李四");
Leader c = new GeneralManager("王五");
Leader d = new ViceGeneralManager("张强");
// 组织责任链对象的关系
a.setNextLeader(b);
b.setNextLeader(c);
c.setNextLeader(d);
// 开始请假操作
LeaveRequest request = new LeaveRequest("TOM", 30, "回家");
a.handleRequest(request);
}
}
迭代器模式(iterator)
场景 :
提供一种可以遍历聚合对象的方式.又称为 : 游标cursor模式;
聚合对象 : 存储数据;
迭代器 : 遍历数据
基本案例 :
实现正向遍历的迭代器;
实现逆向遍历的迭代器;
开发中常见的场景 :
JDK内置的迭代器(List/Set)
例子 :
package com.example.demo.iterator;
/**
* 自定义的迭代器接口
*/
public interface MyIterator {
// 将游标指向第一个元素
void first();
// 将游标指向下一个元素
void next();
// 判断是否存在下一个元素
boolean hasNext();
boolean isFirst();
boolean isLast();
// 获取当前游标指向的对象
Object getCurrentObj();
}
package com.example.demo.iterator;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义的聚合类
*/
public class ConcreteMyAggregate {
private List<Object> list = new ArrayList<Object>();
public ConcreteMyAggregate() {
super();
}
public void addObject(Object obj) {
this.list.add(obj);
}
public void removeObject(Object obj) {
this.list.remove(obj);
}
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
// 获得迭代器
public MyIterator createIterator() {
return new ConcreteIterator();
}
// 使用内部类订阅迭代器,可以直接使用外部类的属性
private class ConcreteIterator implements MyIterator {
// 定义游标用于记录遍历时的位置
private int cursor;
@Override
public void first() {
cursor = 0;
}
@Override
public void next() {
if (cursor < list.size()) {
cursor++;
}
}
@Override
public boolean hasNext() {
if (cursor < list.size()) {
return true;
}
return false;
}
@Override
public boolean isFirst() {
return cursor == 0 ? true : false;
}
@Override
public boolean isLast() {
return cursor == (list.size() - 1) ? true : false;
}
@Override
public Object getCurrentObj() {
return list.get(cursor);
}
}
}
package com.example.demo.iterator;
public class Client {
public static void main(String[] args) {
ConcreteMyAggregate cma = new ConcreteMyAggregate();
cma.addObject("aa");
cma.addObject("bb");
cma.addObject("cc");
MyIterator iterator = cma.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.getCurrentObj());
iterator.next();
}
}
}
中介者模式(Mediator) :
核心 :
如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其复杂,这些对象称为"同事对象";
我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为星形结构.
中介者模式的本质 :
解耦多个同事对象之间的交互关系.每个对象都持有中介者对象的引用,只跟中介者对象打交道.我们通过中介者对象统一管理这些交互关系.
开发中常见的场景 :
MVC模式(其中的C,控制器就是一个中介者对象.M和V都和他打交道);
窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象;
图像界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者对象来解决,可以是整体的窗口对象或者DOM对象.
Java.lang.reflect.Method#invoke()
例子 :
package com.example.demo.mediator;
// 中介者
public interface Mediator {
void register(String dname, Department d);
void command(String dname);
}
package com.example.demo.mediator;
import java.util.HashMap;
import java.util.Map;
public class President implements Mediator {
private Map<String, Department> map = new HashMap<>();
@Override
public void register(String dname, Department d) {
map.put(dname, d);
}
@Override
public void command(String dname) {
map.get(dname);
}
}
package com.example.demo.mediator;
// 同事类的接口
public interface Department {
// 做本部门的事情
void selfAction();
// 向总经理发出申请
void outAction();
}
package com.example.demo.mediator;
public class Development implements Department {
// 持有中介者的引用
private Mediator m;
public Development(Mediator m) {
this.m = m;
m.register("development", this);
}
@Override
public void selfAction() {
System.out.println("汇报工作!没钱了,需要资金支持!");
}
@Override
public void outAction() {
System.out.println("专心科研,开发项目!");
}
}
package com.example.demo.mediator;
public class Finacial implements Department {
// 持有中介者(总经理)的引用
private Mediator m;
public Finacial(Mediator m) {
this.m = m;
m.register("finacial", this);
}
@Override
public void selfAction() {
System.out.println("汇报工作!没钱了,钱太多了!怎么花?");
}
@Override
public void outAction() {
System.out.println("数钱!");
}
}
package com.example.demo.mediator;
public class Market implements Department {
// 持有中介者(总经理)的引用
private Mediator m;
public Market(Mediator m) {
this.m = m;
m.register("market", this);
}
@Override
public void selfAction() {
System.out.println("汇报工作!项目承接的进度,需要资金支持!");
}
@Override
public void outAction() {
System.out.println("跑去接项目!");
m.command("finacial");
}
}
package com.example.demo.mediator;
public class Client {
public static void main(String[] args) {
Mediator m = new President();
Market market = new Market(m);
Development development = new Development(m);
Finacial finacial = new Finacial(m);
market.selfAction();
market.outAction();
}
}
命令模式(command) :
介绍 :
命令模式 : 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作.也称之为 : 动作
Action模式,事务transaction模式;
结构 :
Command抽象命令类;
ConcreteCommand具体命令类;
Invoker调用者/请求者 :
请求的发送者,它通过命令对象来执行请求.一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联.在程序运行时,将调用命令对象的
execute(),间接调用接收者的相关操作.
Receiver接收者 :
接收者执行与请求相关的操作,具体实现对请求的业务处理.
未抽象前,实际执行操作内容的对象.
Client客户类 :
在客户类中需要创建调用者对象,具体命令类对象,在创建具体命令对象时指定对应的接收者.发送者和接收者之间没有直接关系,都通过命令对象间接调用.
开发中常见的场景 :
Struts2,action的整个调用过程中就有命令模式.
数据库事务机制的底层实现;
命令的撤销和恢复.
例子 :
package com.example.demo.command;
/**
* 真正的命令的执行者
*/
public class Receiver {
public void action() {
System.out.println("Receiver.action()");
}
}
package com.example.demo.command;
public interface Command {
/**
* 这个方法是一个返回结果为空的方法.
* 实际项目中,可以根据需求设计多个不同的方法
*/
void execute();
}
class ConcreteCommand implements Command {
// 命令的真正执行者
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
// 命令真正执行前或后,执行相关的处理!
receiver.action();
}
}
package com.example.demo.command;
/**
* 调用者/发起者
*/
public class Invoke {
// 也可以通过容器List<Command>容纳很多命令对象,进行批处理.数据库底层的事务管理就是类似的结构!
private Command command;
public Invoke(Command command) {
this.command = command;
}
// 业务方法,用于调用命令类的方法
public void call() {
command.execute();
}
}
package com.example.demo.command;
public class Client {
public static void main(String[] args) {
Command command = new ConcreteCommand(new Receiver());
Invoke invoke = new Invoke(command);
invoke.call();
}
}
解释器模式(Interpreter)
介绍 :
是一种不常用的设计模式;
用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计.
当我们需要开发一种新的语言时,可以考虑使用解释器模式.
尽量不要使用解释器模式,后期维护会有很大麻烦.在项目中,可以使用Jruby,Groovy,java的js引擎来替代解释器的作用,弥补java语言的不足.
开发中常见的场景 :
EL表达式的处理;正则表达式的解释器;SQL语法的解释器;数学表达式解释器;如现成的工具包 : Math Expression String Parser ,Expression4J等.
MESP的网址 :
Expression4J的网址 :
访问者模式Visitor :
模式动机 :
对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接口),对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访问者其
访问方式也有所不同.
定义 :
表示一个作业于某个对象结构中的各元素的操作,它使我们可以在不改变个元素的类的前提下定义作业于这些元素的新操作.
开发中的场景(应用范围非常窄,了解即可) :
XML文档解析器设计;编译器的设计;复杂集合对象的处理;
策略模式 :
策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法.并且由客户端觉得调用哪个算法.
本质 :
分离算法,选择实现;
开发中常见的场景 :
JAVASE中GUI编程中,布局管理;Spring框架中,Resource接口,资源访问策略;javax.servlet.httpServlet#service();
例子 :
package com.example.demo.strategy;
public interface Strategy {
public double getPrice(double standardPrice);
}
package com.example.demo.strategy;
public class NewCustomerFewStrategy implements Strategy {
@Override
public double getPrice(double standardPrice) {
System.out.println("不打折,原价!");
return standardPrice;
}
}
package com.example.demo.strategy;
public class NewCustomerManyStrategy implements Strategy {
@Override
public double getPrice(double standardPrice) {
System.out.println("打九折!");
return standardPrice*0.9;
}
}
package com.example.demo.strategy;
public class OldCustomerManyStrategy implements Strategy {
@Override
public double getPrice(double standardPrice) {
System.out.println("打八折!");
return standardPrice*0.8;
}
}
package com.example.demo.strategy;
public class OldCustomerFewStrategy implements Strategy {
@Override
public double getPrice(double standardPrice) {
System.out.println("打八五折!");
return standardPrice*0.85;
}
}
package com.example.demo.strategy;
/**
* 负责和具体的策略类交互
* 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化
* 如果使用spring的依赖注入功能,换可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法.
*/
public class Context {
// 当前采用的算法对象
private Strategy strategy;
// 可以通过构造器来注入
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void pringPrice(double s) {
System.out.println("您该报价 : " + strategy.getPrice(s));
}
}
package com.example.demo.strategy;
public class Client {
public static void main(String[] args) {
Strategy s1 = new OldCustomerManyStrategy();
Context context = new Context(s1);
context.pringPrice(998);
}
}
模板方法模式(template method)
模板方法模式介绍 :
模板方法模式是编程中经常用得到模式.它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现.这样,新的子类可以在不改变一个算法结构的前提下重新
定义该算法的某些特定步骤.
核心 :
处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定.因此,我们采用工厂方法模式,将这个节点的代码实现转移给子类完成.既 : 处理步骤父类
中定义好,具体实现延迟到子类中定义.
方法回调(钩子方法)
在好莱坞,当艺人把简历提交给好莱坞的娱乐公司时,所能做的就是等待,整个过程由娱乐公司控制,演员只能被动的服务安排,在需要的时候再由公司安排具体环节的
演出.
在软件开发中,我们可以将call翻译为调用.子类不能调用父类,而通过父类调用子类.这些调用步骤已经在父类中写好了,完全由父类控制整个过程.
什么时候用到模板方法模式 :
实现一个算法时,整体步骤很固定.但是,某些部分易变.易变部分可以抽象出来,供子类实现.
开发中常见的场景 :
非常频繁.各个框架,类库中都有他的影子.比如常见的有 :
数据库访问的封装;Junit单元测试;servlet中关于doGet/doPost方法调用;Hibernate中模板程序;spring中JDBCTemplate,HibernateTemplate等.
例子 :
package com.example.demo.templateMethod;
public abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队!");
}
// 办理具体的业务 //钩子方法
public abstract void transact();
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写.
public final void process() {
this.takeNumber();
// 像个钩子,执行时,挂那个子类的方法就调用哪个.
this.transact();
this.evaluate();
}
}
package com.example.demo.templateMethod;
public class client {
public static void main(String[] args) {
BankTemplateMethod bankTemplateMethod = new DrawMoney();
bankTemplateMethod.process();
System.out.println("----------------------");
// 采用匿名内部类
BankTemplateMethod bankTemplateMethod1 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要存钱");
}
};
bankTemplateMethod1.process();
}
}
class DrawMoney extends BankTemplateMethod {
@Override
public void transact() {
System.out.println("我要取款!!!");
}
}
状态模式(state)
核心 : 用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题;
结构 : Context环境类(环境类中维护一个State对象,他是定义了当前的状态.);State抽象状态类;ConcreteState具体状态类(每一个类封装了一个状态对应的行为);
开发中常见的场景 :
银行系统中账号状态的管理;OA系统中公文状态的管理;酒店系统中,房间状态的管理;线程对象各状态之间的切换.
例子 :
package com.example.demo.state;
public interface State {
void handle();
}
package com.example.demo.state;
/**
* 空闲状态
*/
public class FreeState implements State{
@Override
public void handle() {
System.out.println("房间空闲!!!没人住.");
}
}
package com.example.demo.state;
/**
* 已预订状态
*/
public class BookedState implements State {
@Override
public void handle() {
System.out.println("房间已预订!别人不能定!");
}
}
package com.example.demo.state;
/**
* 已入住状态
*/
public class CheckedInState implements State {
@Override
public void handle() {
System.out.println("房间已入住!请勿打扰!");
}
}
package com.example.demo.state;
/**
* 方法对象
*/
public class Context {
// 如果是银行系统,这个Context类就是账号.根据金额不同,切换不同的状态!
private State state;
public void setState(State s) {
System.out.println("修改状态!");
state = s;
state.handle();
}
}
package com.example.demo.state;
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setState(new FreeState());
context.setState(new BookedState());
context.setState(new CheckedInState());
}
}
观察者模式(Observer) :
核心 :
观察者模式主要用于1 : N的通知.当一个对象(目标对象(消息发布)Subject或Objservable)的状态变化时,他需要及时告知一系列对象(观察者对象(消息订阅),Observer),令他们做出相应;
通知观察者的方式 :
推 : 每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收.
拉 : 观察者只要直到有情况即可.至于什么时候获取内容,获取什么内容,都可以自主决定.
例子 :
package com.example.demo.observer;
public interface Observer {
void update(Subject subject);
}
package com.example.demo.observer;
import java.util.ArrayList;
import java.util.List;
public class Subject {
protected List<Observer> list = new ArrayList<>();
public void registerObserver(Observer obs) {
list.add(obs);
}
public void removeObserver(Observer obs) {
list.remove(obs);
}
//通知所有的观察者更新状态
public void notifyAllObservers() {
for (Observer observer : list) {
observer.update(this);
}
}
}
package com.example.demo.observer;
public class ObserverA implements Observer {
// myState需要跟目标对象的state值保存一致!
private int myState;
@Override
public void update(Subject subject) {
myState = ((ConcreteSubject)subject).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
package com.example.demo.observer;
public class ConcreteSubject extends Subject {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// 主题对象(目标对象)值发生了变化,请通知所有的观察者
this.notifyAllObservers();
}
}
package com.example.demo.observer;
public class Client {
public static void main(String[] args) {
// 目标对象
ConcreteSubject subject = new ConcreteSubject();
// 创建多个观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
// 将这三个观察者添加到subject对象的观察者队伍中
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.registerObserver(obs3);
// 改变subject的状态
subject.setState(3000);
// 我们看看,观察者的状态是不是也发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
System.out.println("------------------");
// 改变subject的状态
subject.setState(30);
// 我们看看,观察者的状态是不是也发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
}
JavaSE提供了java.util.Observable和java.util.Observer来实现观察者模式;
例子 :
package com.example.demo.observer2;
import java.util.Observable;
import java.util.Observer;
public class ObserverA implements Observer {
private int myState;
@Override
public void update(Observable o, Object arg) {
myState = ((ConcreteSubject) o).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
package com.example.demo.observer2;
import java.util.Observable;
public class ConcreteSubject extends Observable {
private int state;
public void set(int a) {
// 目标对象的状态发生了改变
state = a;
// 表示目标对象已经做了更改
setChanged();
// 通知所有的观察者
notifyObservers(state);
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
package com.example.demo.observer2;
public class Client {
public static void main(String[] args) {
// 创建目标对象Obserable
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者
ObserverA observerA = new ObserverA();
ObserverA observerA1 = new ObserverA();
ObserverA observerA2 = new ObserverA();
// 将上面三个观察者对象添加到目标对象subject的观察者容器中
subject.addObserver(observerA);
subject.addObserver(observerA1);
subject.addObserver(observerA2);
// 改变subject对象的状态
subject.set(3000);
System.out.println(observerA.getMyState());
System.out.println(observerA1.getMyState());
System.out.println(observerA2.getMyState());
}
}
开发中常见的场景 :
聊天室程序的,服务器转发给所有客户端;网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发;邮件订阅;Servlet中,监听器的实现;Android中,广播机制;
JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model)
事件源 ----- 目标对象
事件监听器 -----观察者
京东商城中,群发某商品打折信息
备忘录模式(memento) :
核心 : 就是保持某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态.
结构 : 源发器类Originator;备忘录类Memento;负责人类CarTake.
备忘点较多时 :
将备忘录压栈
public class CareTaker {
private Memento memento;
private Stack<Memento> stack = new Stack<Memento>();
}
将多个备忘录对象,序列化和持久化;
开发中常见的应用场景 :
棋类游戏中的,悔棋;普通软件中的,撤销操作;数据库软件中的,事务管理中的,回滚操作;Photoshop软件中的,历史记录
例子 :
package com.example.demo.memento;
/**
* 源发器类
*/
public class Emp {
private String ename;
private int age;
private double salary;
public Emp(String ename, int age, double salary) {
this.ename = ename;
this.age = age;
this.salary = salary;
}
// 进行备忘操作,并返回备忘录对象
public EmpMemento memento() {
return new EmpMemento(this);
}
// 进行数据恢复,恢复成制定备忘录对象的值
public void recovery(EmpMemento mmt) {
this.ename = mmt.getEname();
this.age = mmt.getAge();
this.salary = mmt.getSalary();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.example.demo.memento;
/**
* 负责人类
* 负责管理备忘录对象
*/
public class CareTaker {
private EmpMemento empMemento;
public EmpMemento getEmpMemento() {
return empMemento;
}
public void setEmpMemento(EmpMemento empMemento) {
this.empMemento = empMemento;
}
}
package com.example.demo.memento;
/**
* 备忘录类
*/
public class EmpMemento {
private String ename;
private int age;
private double salary;
public EmpMemento(Emp e) {
this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSalary();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
package com.example.demo.memento;
public class Client {
public static void main(String[] args) {
CareTaker taker = new CareTaker();
Emp emp = new Emp("高琪", 18, 500);
System.out.println("第一打印创建对象 : " + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
// 备忘一次
taker.setEmpMemento(emp.memento());
emp.setAge(38);
emp.setEname("王五");
emp.setSalary(800);
System.out.println("第二次打印创建对象 : " + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
// 恢复到备忘录对象保存的状态
emp.recovery(taker.getEmpMemento());
System.out.println("第三次打印创建对象 : " + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
}
}