1 GOF
《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书),由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著(Addison-Wesley,1995)。这几位作者常被称为"四人组(Gang of Four)",而这本书也就被称为"四人组(或 GoF)"书。
**目的:**便于系统的扩展性、适应不同的情况,也遵守软件设计原则
分类
创建性模式 | 结构性模式 | 行为性模式 |
---|---|---|
单例模式 | 适配器模式 | 责任链模式 |
工厂模式 | 代理模式 | 迭代器模式 |
建造者模式 | 桥接模式 | 中介者模式 |
原型模式 | 组合模式 | 命令模式 |
装饰器模式 | 解释器模式 | |
外观模式 | 访问者模式 | |
享元模式 | 策略模式 | |
模板方法模式 | ||
状态模式 | ||
观察者模式 | ||
备忘录模式 |
创建性模型
单例模式 singleton
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
优点:减少系统资源的占用率,提高访问效率
分类
- 饿汉式
- 懒汉式
- 双重检查式
- 静态内部类式
- 枚举式
应用场景 |
---|
Windows的 Task Manager(任务管理器)就是很典型的单例模式 |
windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例 |
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。 |
网站的计数器,一般也是采用单例模式实现,否则难以同步。 |
应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 |
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。 |
操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 |
Application 也是单例的典型应用(Servlet编程中会涉及到) |
在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理 |
在servlet编程中,每个Servlet也是单例 |
在spring MVC框架/struts1框架中,控制器对象也是单例 |
实现方式
饿汉式实现
类初始化后直接创建实例
package com.szxy.singleton;
/**
* 饿汉式:当类被加载时,创建对象实例
*
*/
public class SingletonDemo1{
/**
* 创建实例对象,天然线程安全
*/
private static SingletonDemo1 instance = new SingletonDemo1();
/**
* 提供私有构造函数,即外部不能通过构造方法,创建该类对象
*/
private SingletonDemo1(){};
/**
* 提供静态方法,供外部访问该类对象的实例
* @return
*/
public static SingletonDemo1 getInstance(){
return instance;
}
}
懒汉式实现
类初始化后,不直接创建实例,等需要的时候,调用方法创建实例
package com.szxy.singleton;
/**
* 懒汉模式:只在被调用的时候才创建对象实例
*
*/
public class SingletonDemo2{
private static SingletonDemo2 instance;
/**
* 提供一个无参的私有构造方法
*/
private SingletonDemo2(){
};
/**
* 使用同步静态方法,供外部获取该类对象的实例
* 使用同步方法,防止线程同时进入 if 语句代码块,导致创建多个对象,违背了单例模式的初衷
* @return
*/
public synchronized static SingletonDemo2 getInstance(){
if(instance == null){
instance = new SingletonDemo2();
}
return instance;
}
}
双重检测锁实现
不推荐使用,由于编译器优化及 JVM 内存模型的原因,可能会出现问题
package com.szxy.singleton;
/**
* 双重检测锁
*
*/
public class SingletonDemo3{
private static SingletonDemo3 instance = null;
private SingletonDemo3() {
}
public static SingletonDemo3 getInstance() {
if(instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if(sc == null) {
synchronized (SingletonDemo3.class) {
if(sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
}
静态内部类实现
兼备了并发高效调用和线程安全,并实现延时加载
package com.szxy.singleton;
/**
* 静态内部类
*
* 线程安全,实现延时加载
*
*/
public class SingletonDemo4{
/****
* 静态内部类,产生单例对象
*
*/
public static class InnerClass {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
public static SingletonDemo4 getInstance() {
return InnerClass.instance;
}
private SingletonDemo4() {
}
}
枚举实现
避免的反射的漏洞,没有延时加载
package com.szxy.singleton;
/**
* 枚举
*/
public enum SingletonDemo5 {
INSTANCE;
public void singletonDemo5Opetion() {
System.out.println("枚举操作...");
}
}
反射和反序列化漏洞
可以通过反射和反序列化方式,获取该类对象
SingletonDemo6.java:
package com.bjsxt.singleton;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 测试懒汉式单例模式(如何防止反射和反序列化漏洞)
* @author 尚学堂高淇 www.sxt.cn
*
*/
public class SingletonDemo6 implements Serializable {
//类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
private static SingletonDemo6 instance;
private SingletonDemo6(){ //私有化构造器
if(instance!=null){
throw new RuntimeException();
}
}
//方法同步,调用效率低!
public static synchronized SingletonDemo6 getInstance(){
if(instance==null){
instance = new SingletonDemo6();
}
return instance;
}
//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
SingletonDemoTest2.java:
package com.szxy.singleton;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
/**
* 反射和反序列漏洞测试
*
*/
public class SingletonDemoTest2 {
public static void main(String[] args) throws Exception {
// 通过反射方式,获取该类的对象
Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.szxy.singleton.SingletonDemo6");
// 获取默认
Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
// 跳过权限检查,否则会 java.lang.IllegalAccessException
c.setAccessible(true);
SingletonDemo6 s6 = c.newInstance();
SingletonDemo6 s7 = c.newInstance();
System.out.println(s6);
System.out.println(s7);
// 通过反序列化,获取该类对象
/*
* ObjectOutputStream oos = new ObjectOutputStream(new
* FileOutputStream("G:/a.txt")); SingletonDemo6 s =
* SingletonDemo6.getInstance(); System.out.println(s); oos.writeObject(s);
* oos.flush(); oos.close();
*
* ObjectInputStream ois = new ObjectInputStream(new
* FileInputStream("G:/a.txt")); SingletonDemo6 ss = (SingletonDemo6)
* ois.readObject(); System.out.println(ss); ois.close();
*/
}
}
效率比较
实现方式 |
---|
饿汉式 |
静态内部类 |
枚举式 |
双重检查锁 |
懒汉式 |
package com.szxy.singleton;
import java.util.concurrent.CountDownLatch;
/**
* 单例模式实现方式性能比较
*
*/
public class SingletonDemoTest3 {
public static void main(String[] args) throws Exception {
int threadNum = 10;
//同步辅助
final CountDownLatch latch = new CountDownLatch(threadNum);
//开始时间
long start = System.currentTimeMillis();
//创建十个线程
for(int i=0;i<10;i++) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<100000;i++) {
/**
* 1 18ms
* 2 44ms
* 3 17ms
* 4 30ms
* 5 13ms
*/
Object obj = SingletonDemo3.getInstance();
//Object obj2 = SingletonDemo5.INSTANCE;
}
//线程执行完毕,计数器简减一
latch.countDown();
}
}).start();
}
//将主线程阻塞,等待其他从线程执行完毕,
latch.await();
System.out.println("总耗时:"+(System.currentTimeMillis()-start)+" ms");
}
}
CutDownLatch 类的 API 使用
作用 | |
---|---|
CutDownLatch 类 | 在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待 |
countDown() | 计数减一(建议放在 finally 代码块中) |
await() | 阻塞当前线程,直到计数器的值为 0 |
选用
选用的规则:线程安全,延时加载,调用效率这三方面考虑。
特点 | 选择 |
---|---|
占用资源少,不需要延时加载 | 枚举式 |
占用资源大,需要延时加载 | 静态内部类 |
工厂模式 Factory
实现创建者与调用者分离
实例化对象,用工厂方法代替 new 操作。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
分类
- 静态工厂模式
- 工厂方法模式
- 抽象工厂模式
面向对象的基本原则:
- OCP(开闭原则,open-closed principle)一个软件的实体应当对扩展开放,对修改关闭。
- DIP(依赖反转原则,Dependency inverse principle)要针对接口编程, 不要针对实现编程。
- LoD(迪米特法则,Law of Demeter):只与你直接的朋友通信,而避免和 陌生人通信。
实现
简单工厂模式
SimpleFactory.java:
package com.szxy.simplefacotry;
/**
* 简单工厂模式实现
*
*/
public class AnimalSimpleFactory {
public final static String CAT_TYPE = "cat";
public final static String DOG_TYPE = "dog";
public static Animal getAnimal(String type) {
if(type.equals(CAT_TYPE)) {
return new Cat();
}else if(type.equals(DOG_TYPE)){
return new Dog();
}else {
return null;
}
}
}
AnimalSimpleFactory2.java:
package com.szxy.simplefacotry;
public class AnimalSimpleFactory2 {
public static Animal getCat() {
return new Cat();
}
public static Animal getDog() {
return new Dog();
}
}
工广方法模式
AnimalFactory.java:
package com.szxy.factoryMethod;
/**
* 抽象工厂模式接口
*
*/
public interface AnimalFactory {
public Animal create();
}
BirdFactory.java:
package com.szxy.factoryMethod;
public class BirdFactory implements AnimalFactory {
@Override
public Animal create() {
return new Bird();
}
}
FactoryMethodTest.java:
package com.szxy.factoryMethod;
public class FactoryMethodTest {
public static void main(String[] args) {
Animal cat = new CatFactory().create();
Animal dog = new DogFactory().create();
Animal bird = new BirdFactory().create();
cat.shut();
dog.shut();
bird.shut();
}
}
抽象工厂模式
关于 Engine
package com.szxy.abstractFactory;
public interface Engine {
public void run();
}
class LowEngine implements Engine{
@Override
public void run() {
System.out.println("转速慢....");
}
}
class LuxuryEngine implements Engine{
@Override
public void run() {
System.out.println("转速贼快 ....");
}
}
关于座椅
package com.szxy.abstractFactory;
public interface Seat {
public void seat();
}
class LowSeat implements Seat{
@Override
public void seat() {
System.out.println("坐着难受 ...");
}
}
class LuxurySeat implements Seat{
@Override
public void seat() {
System.out.println("坐着舒适...");
}
}
关于轮胎
package com.szxy.abstractFactory;
public interface Tyre {
public void revole();
}
class LowTyre implements Tyre{
@Override
public void revole() {
System.out.println("易磨损...");
}
}
class LuxuryTyre implements Tyre{
@Override
public void revole() {
System.out.println("抗磨损...");
}
}
CarFactory.java
package com.szxy.abstractFactory;
/**
* 车辆工厂,这里可以组装不同的类型,种类的汽车
*
*/
public interface CarFactory {
public Engine createEngine();
public Seat createSeat();
public Tyre createTyre();
}
LowCarFactory.java
package com.szxy.abstractFactory;
/**
* 生产低端汽车
*
*/
public class LowCarFactory implements CarFactory {
@Override
public Engine createEngine() {
return new LowEngine();
}
@Override
public Seat createSeat() {
return new LowSeat();
}
@Override
public Tyre createTyre() {
return new LowTyre();
}
}
LuxuryCarFactory.java
package com.szxy.abstractFactory;
/**
* 生产高端汽车
*
*/
public class LuxuryCarFactory implements CarFactory {
@Override
public Engine createEngine() {
return new LuxuryEngine();
}
@Override
public Seat createSeat() {
return new LuxurySeat();
}
@Override
public Tyre createTyre() {
return new LuxuryTyre();
}
}
AbstractFactoryTest.java
package com.szxy.abstractFactory;
public class AbstractFactoryTest {
public static void main(String[] args) {
LowCarFactory factory = new LowCarFactory();
Engine engine = factory.createEngine();
Seat seat = factory.createSeat();
Tyre tyre = factory.createTyre();
engine.run();
seat.seat();
tyre.revole();
}
}
总结
工厂模式 | 要点 |
---|---|
简单工厂模式 | 虽然某种程度不符合设计原则,但实际使用最多。 |
工厂方法模式 | 不修改已有类的前提下,通过增加新的工厂类实现扩展。 |
抽象工厂模式 | 不可以增加产品,可以增加产品族! |
应用场景 | JDK中Calendar的getInstance方法 JDBC中Connection对象的获取 Hibernate中SessionFactory创建Session spring中IOC容器创建管理bean对象 XML解析时的DocumentBuilderFactory创建解析器对象 反射中Class对象的newInstance() |
建造者模式 Builder
分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;
相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配
算法的解耦,实现了更好的复用。
实现
应用场景
- StringBuilder类的append方法
- SQL中的PreparedStatement
- JDOM中,DomBuilder、SAXBuilder
原型模式 prototype
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
– 就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
– 优势有:效率高(直接克隆,避免了重新执行构造过程步骤) 。
– 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的
对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,
再修改克隆对象的值。
分类
-
浅拷贝
被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都
仍然指向原来的对象。
-
深拷贝
深克隆把引用的变量指向复制过的新对象,而不是原有的被引用的对象。
深克隆:让已实现Clonable接口的类中的属性也实现Clonable接口
基本数据类型和String能够自动实现深度克隆(值的复制)
实现
Cloneable接口和clone方法
Prototype模式中实现起来最困难的地方就是内存复制操作,所幸在Java中提供了 clone()方法替我们做了绝大部分事情。
浅拷贝
package com.szxy.prototype;
import java.util.Date;
/**
*
*
*/
public class Sheep implements Cloneable {
private String name;
private Date date;
/*******
* 浅拷贝
******/
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
return obj;
}
public Sheep() {
}
public Sheep(String name, Date date) {
this.name = name;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
浅拷贝测试
package com.szxy.prototype;
import java.util.Date;
public class ShadowCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
Sheep s = new Sheep("懒懒羊",date);
System.out.println(s);
System.out.println(s.getName());
System.out.println(s.getDate());
date.setTime(11111111111111L);
System.out.println(s.getDate());
Sheep copy = (Sheep) s.clone();
copy.setName("美洋洋");
System.out.println(copy);
System.out.println(copy.getName());
System.out.println(copy.getDate());
}
}
/*
结果
com.szxy.prototype.Sheep@15db9742
懒懒羊
Thu Jul 25 20:30:24 CST 2019
Mon Feb 06 03:45:11 CST 2322
com.szxy.prototype.Sheep@5c647e05
美洋洋
Mon Feb 06 03:45:11 CST 2322
*/
深拷贝
package com.szxy.prototype;
import java.util.Date;
/**
*
* 深拷贝
*/
public class Sheep2 implements Cloneable {
private String name;
private Date date;
/*******
* 深拷贝
******/
@Override
protected Object clone() throws CloneNotSupportedException {
//得到克隆的副本
Sheep2 obj = (Sheep2) super.clone();
//将属性也克隆
Date date = (Date) obj.getDate().clone();
obj.setDate(date);
return obj;
}
public Sheep2() {
}
public Sheep2(String name, Date date) {
super();
this.name = name;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
深拷贝测试
package com.szxy.prototype;
import java.util.Date;
public class DeepCopyTest {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
Sheep2 s = new Sheep2("懒羊羊",date);
Sheep2 copy = (Sheep2) s.clone();
copy.setName("喜洋洋");
System.out.println(s);
System.out.println(s.getName());
System.out.println(s.getDate());
date.setTime(111111111111L);
System.out.println(s.getDate());
System.out.println(copy);
System.out.println(copy.getName());
System.out.println(copy.getDate());
}
}
/***
com.szxy.prototype.Sheep2@15db9742
懒羊羊
Thu Jul 25 20:31:51 CST 2019
Tue Jul 10 08:11:51 CST 1973
com.szxy.prototype.Sheep2@5c647e05
喜洋洋
Thu Jul 25 20:31:51 CST 2019
***/
使用序列化与反序列化实现深拷贝
package com.szxy.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
public class SerializableDeepCopyTest {
public static void main(String[] args) throws Exception {
Date date = new Date();
Sheep s = new Sheep("懒懒羊",date);
System.out.println(s);
System.out.println(s.getName());
System.out.println(s.getDate());
//使用序列化与反序列实现深拷贝
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(s);
//将序列化的结果输出到字节数组中
byte[] byteArray = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bis);
Sheep copy = (Sheep) ois.readObject();
date.setTime(11111111111111L);
System.out.println(s.getDate());
//Sheep copy = (Sheep) s.clone();
copy.setName("美洋洋");
System.out.println(copy);
System.out.println(copy.getName());
System.out.println(copy.getDate());
}
}
应用场景
开发中应用场景原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
- spring中bean的创建实际就是两种:单例模式和原型模式。(当然,原型模式需要和工厂模式搭配起来)
结构性模式
适配器模式 Adapter
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
• 模式中的角色
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象 的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成 目标接口。
实现
应用场景
字节转为字节流
类型 | 使用 |
---|---|
工作场景 | 经常用来做旧系统改造和升级 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍。 |
学习场景 | java.io.InputStreamReader(InputStream) java.io.OutputStreamWriter(OutputStream) |
代理模式 proxy
核心作用: 通过代理,控制对对象的访问!
可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后 做后置处理。(即:AOP的微观实现!)
– AOP(Aspect Oriented Programming面向切面编程)的核心实现机制
分类
- 静态代理
- 动态代理(JDK 代理、CGLIB 代理)
实现
静态代理
动态代理
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一
的处理众多的方法。
JDK自带的动态代理
– java.lang.reflect.Proxy
• 作用:动态生成代理类和对象
– java.lang.reflect.InvocationHandler(处理器接口)
• 可以通过invoke方法实现对真实角色的代理访问。
• 每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
public static void realizeJDKDynamic() {
Star zhouDong = new ZhouDong();
//JDK 动态生成代理类
Star proxy = (Star) Proxy.newProxyInstance(Star.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("面谈,签约,签字..");
method.invoke(zhouDong, args);
System.out.println("收尾款...");
return null;
}
});
//代用代理类方法
proxy.sing();
proxy.signContact();
}
应用场景
– 安全代理:屏蔽对真实角色的直接访问。
– 远程代理:通过代理类处理远程方法调用(RMI)
– 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
开发框架的应用场景
- struts2中拦截器的实现
- 数据库连接池关闭处理
- Hibernate中延时加载的实现
- mybatis中实现拦截器插件
- AspectJ的实现
- spring中AOP的实现
- 日志拦截
- 声明式事务处理
- Web Service
- RMI远程方法调用
桥接模式 brider
作用:解决多继承结构的,类个数膨胀的问题去,而且违背了单一职责原则
桥接模式核心要点:
处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
实现
应用场景
JDBC驱动程序
– AWT中的Peer架构
– 银行日志管理:
• 格式分类:操作日志、交易日志、异常日志
• 距离分类:本地记录日志、异地记录日志
– 人力资源系统中的奖金计算模块:
• 奖金分类:个人奖金、团体奖金、激励奖金。
• 部门分类:人事部门、销售部门、研发部门。
– OA系统中的消息处理:
• 业务类型:普通消息、加急消息、特急消息
• 发送消息方式:系统内消息、手机短信、邮件• 使用组合模式的场景:
– 把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对
象和整体对象。
组合模式 Composite
适用于处理树形结构
• 使用组合模式的场景:
把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。
组合模式核心
- 抽象构件(Component)角色: 定义了叶子和容器构件的共同点
- 叶子(Leaf)构件角色:无子节点
- 容器(Composite)构件角色: 有容器特征,可以包含子节点
实现
Componet.java:
package com.szxy.composite;
/**
* 组件
*
*/
//抽象组件:定义叶子和容器构建的共同点
public interface Componet {
void operate();
}
//节点:没有孩子
interface Leaf extends Componet{
}
//容器构建角色:有容器特征,可以包含子节点
interface Composite extends Componet{
public void add(Componet componet);
public void remove(int index);
public Componet getChild(int index);
}
Folder.java:
package com.szxy.composite;
import java.util.ArrayList;
import java.util.List;
public class Folder implements Composite{
//文件夹名字
private String name;
//子文件或文件夹
private List<Componet> list = new ArrayList<Componet>();
public Folder(String name) {
this.name = name;
}
public Folder() {
super();
}
@Override
public void operate() {
System.out.println("对"+name+"文件夹进行杀毒...");
for (Componet componet : list) {
componet.operate();
}
}
@Override
public void add(Componet componet) {
list.add(componet);
}
@Override
public void remove(int index) {
list.remove(index);
}
@Override
public Componet getChild(int index) {
return list.get(index);
}
}
class TxtFile implements Leaf{
private String name;
public TxtFile(String name) {
this.name = name;
}
public TxtFile() {
}
@Override
public void operate() {
System.out.println("--对文件"+name+"进行杀毒 ....");
}
}
class ImgFile implements Leaf{
private String name;
public ImgFile(String name) {
super();
this.name = name;
}
public ImgFile() {
super();
}
@Override
public void operate() {
System.out.println("--对图片"+name+"进行杀毒 ....");
}
}
class VideoFile implements Leaf{
private String name;
public VideoFile(String name) {
super();
this.name = name;
}
public VideoFile() {
super();
}
@Override
public void operate() {
System.out.println("--对视频"+name+"进行杀毒 ....");
}
}
Client.java:
package com.szxy.composite;
public class Client {
public static void main(String[] args) {
Composite f1 = new Folder("我的资源");
Componet f2 = new ImgFile("秀儿");
Componet f3 = new TxtFile("HelloWorld.txt");
Componet f4 = new VideoFile("xxx.mp4");
f1.add(f2);
f1.add(f3);
f1.add(f4);
Composite f11 = new Folder("我的资源");
Componet f22 = new ImgFile("蒂花之秀...");
f11.add(f22);
f1.add(f11);
f1.operate();
}
}
//运行结果
对我的资源文件夹进行杀毒...
--对图片秀儿进行杀毒 ....
--对文件HelloWorld.txt进行杀毒 ....
--对视频xxx.mp4进行杀毒 ....
对我的资源文件夹进行杀毒...
--对图片蒂花之秀...进行杀毒 ....
应用场景
- 操作系统的资源管理器
- GUI中的容器层次图
- XML文件解析
- OA系统中,组织结构的处理
- Junit单元测试框架
- 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)
- Test 接口(抽象)
装饰器模式 decorator
动态为一个对象增加新功能
装饰模式是一种用于代替继承的技术,无须通过继承增加子类的就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀
实现
ICar.java:
package com.szxy.decorator;
public interface ICar {
void move();
}
class Car implements ICar{
@Override
public void move() {
System.out.println("陆地上跑 ....");
}
}
SuperCar.java:
package com.szxy.decorator;
//装饰器
public class SuperCar implements ICar{
protected ICar car;
@Override
public void move() {
car.move();
}
}
//具体装饰
class WaterCar extends SuperCar{
public WaterCar() {
}
public WaterCar(ICar car) {
super.car =car;
}
public void swim() {
System.out.println("水上漂....");
}
@Override
public void move() {
super.move();
swim();
}
}
class FlyCar extends SuperCar{
public FlyCar(ICar car) {
super.car = car;
}
public void fly() {
System.out.println("天上飞 ....");
}
@Override
public void move() {
super.move();
fly();
}
}
class AICar extends SuperCar{
public AICar(ICar car) {
super.car = car;
}
public void autoDriver() {
System.out.println("自动驾驶 ...");
}
@Override
public void move() {
super.move();
autoDriver();
}
}
Client.java:
package com.szxy.decorator;
public class Client {
public static void main(String[] args) {
ICar car = new Car();
car.move();
System.out.println("添加水上漂的功能 ...");
WaterCar wCar = new WaterCar(car);
wCar.move();
System.out.println("添加自动驾驶的功能 ...");
AICar aCar = new AICar(car);
aCar.move();
System.out.println("添加三个功能 ...");
AICar c = new AICar(new WaterCar(new FlyCar(new Car())));
c.move();
//运行结果
/*********
陆地上跑 ....
添加水上漂的功能 ...
陆地上跑 ....
水上漂....
添加自动驾驶的功能 ...
陆地上跑 ....
自动驾驶 ...
添加三个功能 ...
陆地上跑 ....
天上飞 ....
水上漂....
自动驾驶 ...
**********/
}
}
应用场景
- IO中输入流和输出流的设计
- Swing包中图形界面构件功能
- Servlet API 中提供了一个request对象的Decorator设计模式的默认实 现类HttpServletRequestWrapper,HttpServletRequestWrapper 类,增强了request对象的功能。
- Struts2中,request,response,session对象的处理
装饰器模式与桥接模式的区别
两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装 饰模式是为了增加新的功能。
总结
总结 | 装饰模式(Decorator)也叫包装器模式(Wrapper) 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并 使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新 的具体构建类和具体装饰类。 |
---|---|
优点 | 扩展对象功能,比继承灵活,不会导致类个数急剧增加 – 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更 加强大的对象 – 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加 新的具体构件子类和具体装饰子类。 |
缺点 | 产生很多小对象。大量小对象占据内存,一定程度上影响性能。 – 装饰模式易于出错,调试排查比较麻烦。 |
外观模式 facade
为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
迪米特法则(最少知识原则):
– 一个软件实体应当尽可能少的与其他实体发生相互作用。
实现
应用场景
频率很高。哪里都会遇到。各种技术和框架中,都 有外观模式的使用。如:
• JDBC封装后的,commons提供的DBUtils类,
Hibernate提供的工具类、Spring JDBC工具类等
享元模式 Flyweight
享元模式以共享的方式高效地支持大量细粒度对象的重用。
– 享元对象能做到共享的关键是区分了内部状态和外部状态。
• 内部状态:可以共享,不会随环境变化而改变
• 外部状态:不可以共享,会随环境变化而改变
实现
应用场景
享元模式开发中应用的场景:
- 享元模式由于其共享的特性,可以在任何“池”中操作, 比如:线程池、数据库连接池。
- String类的设计也是享元模式
行为性模式
责任链 chain of Responsibility
将能够处理通一个类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。
实现
LeaveRequest.java:
package com.szxy.chainOfResponsibility;
public class LeaveRequest {
//请假人名字
private String name;
//请假天数
private int days;
//请假理由
private String reason;
public LeaveRequest() {
super();
// TODO Auto-generated constructor stub
}
public LeaveRequest(String name, int days, String reason) {
super();
this.name = name;
this.days = days;
this.reason = reason;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
Leader.java:
package com.szxy.chainOfResponsibility;
public abstract class Leader {
private String Name; // 处理人名字
private Leader nextLeader;// 下一个处理人
//处理请求
public abstract void handlerReq(LeaveRequest req);
public Leader() {
}
public Leader(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public Leader getNextLeader() {
return nextLeader;
}
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
}
Director.java:
package com.szxy.chainOfResponsibility;
//主管
public class Director extends Leader{
public Director() {
super();
}
public Director(String name) {
super(name);
}
@Override
public void handlerReq(LeaveRequest req) {
int days = req.getDays();
if(days < 3) {
System.out.println("请假人:"+req.getName()+" 请假天数:"+req.getDays()+"天 请假理由:"+req.getReason());
System.out.println("主任:"+this.getName()+" 批准通过!!!");
}else {
if(this.getNextLeader() != null) {
this.getNextLeader().handlerReq(req);
}else {
System.out.println("请求临时不能被处理 ...");
}
}
}
}
Manager.java:
package com.szxy.chainOfResponsibility;
//经理
public class Manager extends Leader{
public Manager() {
super();
}
public Manager(String name) {
super(name);
}
@Override
public void handlerReq(LeaveRequest req) {
int days = req.getDays();
if(days < 10) {
System.out.println("请假人:"+req.getName()+" 请假天数:"+req.getDays()+"天 请假理由:"+req.getReason());
System.out.println("经理:"+this.getName()+" 批准通过!!!");
}else {
if(this.getNextLeader() != null) {
this.getNextLeader().handlerReq(req);
}else {
System.out.println("请求临时不能被处理 ...");
}
}
}
}
GeneralManager.java:
package com.szxy.chainOfResponsibility;
//总经理
public class GeneralManager extends Leader{
public GeneralManager() {
super();
}
public GeneralManager(String name) {
super(name);
}
@Override
public void handlerReq(LeaveRequest req) {
int days = req.getDays();
if(days < 30) {
System.out.println("请假人:"+req.getName()+" 请假天数:"+req.getDays()+"天 请假理由:"+req.getReason());
System.out.println("总经理:"+this.getName()+" 批准通过!!!");
}else {
System.out.println(req.getName()+" 还想请假,不行!!!,去加班 966");
}
}
}
Client.java:
package com.szxy.chainOfResponsibility;
public class Client {
public static void main(String[] args) {
LeaveRequest req = new LeaveRequest("小熊爱吃蜂蜜", 16, "小熊回家吃蜂蜜");
Leader director = new Director("张三");
Leader manager = new Manager("李四");
Leader GeneralManager = new GeneralManager("王五");
//设置责任链
director.setNextLeader(manager);
manager.setNextLeader(GeneralManager);
director.handlerReq(req);
}
}
应用场景
-
Java中,异常机制就是一种责任链模式。一个try可以对应多个catch,
当第一个catch不匹配类型,则自动跳到第二个catch.
-
Javascript语言中,事件的冒泡和捕获机制。Java语言中,事件的处理 采用观察者模式。
-
Servlet开发中,过滤器的链式处理
-
Struts2中,拦截器的调用也是典型的责任链模式
迭代器模式 iterator
提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
聚合对象:存储数据
迭代器:遍历数据
实现
应用场景
- JDK内置的迭代器(List/Set)
中介者模式
如果一个系统中对象之间的联系呈现网状结构,对象之间存在大量多对多关系,将导致关系极其复杂,这些对象称为“同事对象”
我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星型结构
本质:解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引 用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互关系
实现
应用场景
- MVC模式(其中的C,控制器就是一个中介者对象。M和V都和他打交 道)
- 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象
- 图形界面开发GUI中,多个组件之间的交互,可以通过引入一个中介者 对象来解决,可以是整体的窗口对象或者DOM对象
- Java.lang.reflect.Method#invoke()
命令模式 command
将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化对对象请求排队或者记录请求日志,以及支持可撤销的操作。 也称也:动作 Action 模式、事务 Transaction 模式
实现
应用场景
Struts2中,action的整个调用过程中就有命令模式。
数据库事务机制的底层实现
命令的撤销和恢复
解释器模式 interpreter
用于设计新的编程语言
用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的 编译器和解释器设计。
当我们需要开发一种新的语言时,可以考虑使用解释器模式。
尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中,可以使用
Jruby,Groovy、java的js引擎来替代解释器的作用,弥补java语言的不足。
应用场景
- EL表达式式的处理
- 正则表达式解释器
- SQL语法的解释器
- 数学表达式解析器
- 如现成的工具包:Math Expression String Parser、Expression4J等。
- MESP的网址: http://sourceforge.net/projects/expression-tree/
- Expression4J的网址: http://sourceforge.net/projects/expression4j/
访问者模式 visitor
模式动机:
– 对于存储在一个集合中的对象,他们可能具有不同的类型(即使有一个公共的接 口),对于该集合中的对象,可以接受一类称为访问者的对象来访问,不同的访 问者其访问方式也有所不同。
定义:
表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变个元素 的类的前提下定义作用于这些元素的新操作。
应用场景
- XML文档解析器设计
- 编译器的设计
- 复杂集合对象的处理
策略模式 strategy
分离算法,选择实现
策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族 中任选一个算法解决某一问题,同时可以方便的更换算法或者增加新的算法。并且由客户端决定调用哪个算法。
实现
Strategy.java
package com.szxy.strategy;
//策略
public interface Strategy {
public double HandlePrice(double price);
}
Context.java
package com.szxy.strategy;
//上下文
public class Context {
//算法
private Strategy strategy;
//构造器注入
public Context(Strategy strategy) {
this.strategy = strategy;
}
// setter 方法注入
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public double useStrategy(double price) {
return strategy.HandlePrice(price);
}
}
Client.java
package com.szxy.strategy;
public class Client {
public static void main(String[] args) {
Strategy mnus = new ManyNewUserStrategy();
Strategy mous = new ManyOldUserStrategy();
Strategy fnus = new FewNewUserStrategy();
Strategy fous = new FewOldUserStrategy();
Context context = new Context(mnus);
double newPrice = context.useStrategy(100);
System.out.println(newPrice);
}
}
应用场景
- JAVASE中GUI编程中,布局管理
- Spring框架中,Resource接口,资源访问策略
- javax.servlet.http.HttpServlet#service()
模板方法模式 template method 😈
处理步骤父类中定义好,具体实现延迟到子类中定义
实现
BankTemplateMethod.java:模拟客户到银行进行办理业务的固定流程,其中办理具体业务,延迟到子类实现
package com.szxy.templateMethod;
/**
* 银行 模板方法
* @author zwer
*
*/
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();
}
}
Client.java:客户到银行办理业务的具体流程操作实现
package com.szxy.templateMethod;
public class Client {
public static void main(String[] args) {
BankTemplateMethod method = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要取钱");
}
};
BankTemplateMethod method2 = new BankTemplateMethod() {
@Override
public void transact() {
System.out.println("我要存钱");
}
};
method2.process();
}
}
应用场景
实现一个算法时,整体步骤很固定。但是,某些部分易变。易变部分可以抽象成出来,供子类实现
- 开发中常用的场景
- 数据库访问的封装
- Junit 测试
- Servlet 中关于 doGet 和 doPost 方法的调用
- Hibernate 模板程序
- Spring 中 JDBC Template 、HibernateTemplate
状态模式 state
用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题
实现
下面宾馆房间状态图,描述宾馆房间状态的变化过程。
下面的状态模式的实现 Demo 栗子的宾馆房间类图
- HomeContext 房间上下文类,上下文中维护一个 State 对象,它是定义了当前的状态
- State 抽象状态类
- ConcreteState 具体状态类(如:BookedHomeState.java),每个类封装了一个状态对应的行为
、
定义状态处理接口 State.java:
package com.szxy.state;
public interface State {
public void handle();
}
HomeContext.java
package com.szxy.state;
//房间对象
public class HomeContext {
private State state;//房间的状态
public void setSate(State state) {
System.out.println("状态改变...");
this.state = state;
state.handle();
}
}
应用场景
-
银行系统中账号状态的管理
-
OA系统中公文状态的管理
-
酒店系统中,房间状态的管理
-
线程对象各状态之间的切换
观察者模式 Observe
观察者:多个订阅者、客户
目标:需要同步给多个订阅者的数据封装到对象中
观察者模式用于 1:N 的通知。当一个对象(目标对象 Subject 或者 Observable)的状态变化时,它需要及时告知一系列对象(观察者对象,Observer),令它们做出响应
通知观察者的方式:
- 推:每次都会把通知以广播方式发送所有观察者,所有观察者只能被动接受
- 拉:观察者知道有情况即可。至于什么时候获取内容,获取什么内容,可以由自主决定
JDK 提供 java.util.Observable
和 `` java.util.Observer` 来实现观察者模式
实现
Subject.java:目标对象
package com.szxy.observer;
import java.util.ArrayList;
import java.util.List;
public class Subject {
//存放观察者对象
protected List<Observer> list = new ArrayList<Observer>();
//添加新的观察者对象
public void addObserverObject(Observer obs) {
list.add(obs);
}
//移除已存在的观察者对象
public void removeObserverObject(Observer obs) {
list.remove(obs);
}
//通知观察者更新自身状态
public void notifyAllObserverObject() {
for (Observer observer : list) {
observer.update(this);
}
}
}
Observer.java:观察者
package com.szxy.observer;
//观察者接口
public interface Observer {
//根据目标对象的广播,更新观察者的状态
public void update(Subject sub);
}
ConcreteSubject.java: 目的对象具体实现
package com.szxy.observer;
public class ConcreteSubject extends Subject{
private int state;//目的对象的状态值
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
//通知观察者
this.notifyAllObserverObject();
}
}
ObserverA.java: 观察者具体实现
package com.szxy.observer;
public class ObserverA implements Observer{
private int myState; //观察者的状态值
@Override
public void update(Subject sub) {
this.myState = ((ConcreteSubject)sub).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
应用场景
-
聊天室程序的,服务器转发给所有客户端
-
网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
-
邮件订阅
-
Servlet中,监听器的实现
-
Android中,广播机制
-
JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event Model)
-
事件源----------------目标对象
-
事件监听器------------观察者
-
京东商城中,群发某商品打折信息
备忘录模式 memento
就是保存某个对象内部状态的拷贝,这样以后就可以将该对象恢复到原先的状态。
实现
Emp.java: 要被备份的对象
package com.szxy.memento;
public class Emp {
private String name;
private int age;
private double salary;
// 备份
public EmpMemento memento() {
return new EmpMemento(this);
}
// 恢复
public void recovery(EmpMemento memento) {
this.name = memento.getName();
this.age = memento.getAge();
this.salary = memento.getSalary();
}
public Emp(String name, int age, double salary) {
super();
this.name = name;
this.age = age;
this.salary = salary;
}
public Emp() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Emp [name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
EmpMemento.java: 备份 Emp 对象的数据
package com.szxy.memento;
public class EmpMemento {
private String name;
private int age;
private double salary;
public EmpMemento() {
}
public EmpMemento(Emp emp) {
this.name = emp.getName();
this.age = emp.getAge();
this.salary = emp.getSalary();
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
CareTaker.java:管理备忘录
package com.szxy.memento;
import java.util.ArrayList;
import java.util.List;
//负责人 ,管理备忘录
public class CareTaker {
//单个备忘点
private EmpMemento empMemento;
//通过 List 集合,保存多个备忘点
private List<EmpMemento> mementos = new ArrayList<>();
public void addMemento( EmpMemento empMemento) {
mementos.add(empMemento);
}
public void removeMemento(EmpMemento empMemento) {
mementos.remove(empMemento);
}
public EmpMemento getMementoFromList(int index) {
return mementos.get(index);
}
public List<EmpMemento> getMementos() {
return mementos;
}
public EmpMemento getEmpMemento() {
return empMemento;
}
public void setEmpMemento(EmpMemento empMemento) {
this.empMemento = empMemento;
}
}
Client.java:测试
package com.szxy.memento;
public class Client {
public static void main(String[] args) {
Emp emp = new Emp("小猪佩奇",12,-999);
System.out.println("创建:"+emp);
EmpMemento memento = emp.memento();//备份
CareTaker careTaker = new CareTaker();
//careTaker.setEmpMemento(memento);
careTaker.addMemento(memento);//备份一
emp.setName("琪琪");
emp.setAge(18);
emp.setSalary(2000);
EmpMemento memento2 = emp.memento();
careTaker.addMemento(memento2); //备份二
System.out.println("修改:"+emp);
emp.setName("美琪");
emp.setAge(19);
emp.setSalary(1111111L);
System.out.println("修改:"+emp);
//恢复
//EmpMemento recoverEmp = careTaker.getEmpMemento();
EmpMemento recoverEmp = careTaker.getMementoFromList(1);
emp.recovery(recoverEmp);
System.out.println("恢复:"+emp);
/*************
创建:Emp [name=小猪佩奇, age=12, salary=-999.0]
修改:Emp [name=琪琪, age=18, salary=2000.0]
修改:Emp [name=美琪, age=19, salary=1111111.0]
恢复:Emp [name=琪琪, age=18, salary=2000.0]
**************/
}
}
应用场景
-
棋类游戏中的,悔棋
-
普通软件中的,撤销操作
-
数据库软件中的,事务管理中的,回滚操作
-
Photoshop软件中的,历史记录