目录
一.单例模式
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。
懒汉模式:延时加载,只有在真正使用到的时候,才进行实例化。
public class LazySingleton {private volatile static LazySingleton instance = null;private LazySingleton() {}public static LazySingleton getInstance() {if(null == instance) {synchronized (LazySingleton.class) {if(null == instance) {instance = new LazySingleton();}}}return instance;}}
(1)线程安全问题
(2)double check加锁优化
(3)编译器(JIT),CPU对满足as-if-series的指令会进行指令重排,会导致获取到未初始化的实例,所以需要加上volatile关键字进行修饰,防止指令重排(创建一个对象,需要经过开辟空间、赋默认值、初始化的过程)。
饿汉模式:
只有在真正主动使用对应的类时,才会触发初始化,例如(当前类时启动类即main函数所在类,直接进行new操作,访问静态属性、访问静态方法,用反射访问类等);类加载的初始化阶段就完成了实例的初始化,本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。
类加载过程:
(1)加载二进制数据到内存中,生成对应的class数据结构
(2)连接:a.验证,b.准备(给类的静态成员变量赋默认值),c.解析
(2)初始化:给类的静态变量赋初值
public class HungrySingleton {public static String name = "qingyun";private static HungrySingleton instance = new HungrySingleton();private HungrySingleton() {}public static HungrySingleton getInstance() {return instance;}}
使用静态内部类实现饿汉模式:
public class InnerClassSingleton {public static String name= "qingyun";private InnerClassSingleton() {}private static class SingletonHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return SingletonHolder.instance;}}
当访问静态name属性时,不会加载instance的初始化,只有真正使用到对应的类时,才会初始化,例如调用InnerClassSingleton.getInstance()。
要想反编译查看类加载的过程,可以通过idea的Terminal查看,进入class所在的目录,执行javap命令:
使用序列化、反序列化的方式创建单例,需要保证类的唯一性。
(1)把类序列化
类需要实现Serializable接口,并且需要设置serialVersionUID 的值,使用jdk自带的ObjectOutputStream把类序列化到磁盘上。
public class LazySingletonMain { public static void main(String[] args) { try { //获取实体 LazySingleton instance = LazySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("name")); oos.writeObject(instance); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }}class LazySingleton implements Serializable { private static final long serialVersionUID = 1L; private volatile static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if(null == instance) { synchronized (LazySingleton.class) { if(null == instance) { instance = new LazySingleton(); } } } return instance; }}
执行完后会生产序列化的文件
(2)把类反序列化,保证是单例的
使用jdk自带的ObjectInputStream把序列化的类反序列化,这样使用序列化的方式进行单例创建才能保证唯一性。
public class LazySingletonMain { public static void main(String[] args) { try { LazySingleton instance = LazySingleton.getInstance(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("name")); //反序列化实体 LazySingleton lazySingleton = (LazySingleton)ois.readObject(); System.out.println(lazySingleton == instance); //输出true } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}class LazySingleton implements Serializable { //需要制定版本,才能在序列化和反序列化中匹配为同一个实体 private static final long serialVersionUID = 1L; private volatile static LazySingleton instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if(null == instance) { synchronized (LazySingleton.class) { if(null == instance) { instance = new LazySingleton(); } } } return instance; } //反序列化的时候,生成实体时执行此处代码 private Object readResolve() throws ObjectStreamException { return getInstance(); }}
二.工厂方法模式
Factory Method 定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式使得一个类的实例化延时到子类。
应用场景:
当你不知道该使用对象的确切类型的时候;
当你希望为库或框架提供扩展其它内部组件的方法时。
主要优点:
将具体产品和创建者解耦;
符合单一职责原则;
符合开闭原则。
public class FactoryMethod { public static void main(String[] args) { //创建子类,使用继承的父类来接收 //Application application = new CreateProductA(); Application application = new CreateProductB(); //使用接口类来接收创建出来类,体现了多态 Product productObject = application.getProduct(); //创建的是哪个类,调用到的就是哪个类的方法实现 productObject.method(); }}//定义一个接口类interface Product{ public void method();}//类实现了接口,必须实现接口中的方法class ProductA implements Product{ @Override public void method() { System.out.println("ProductA method"); }}class ProductB implements Product{ @Override public void method() { System.out.println("ProductB method"); }}//抽象类里面不一定要有抽象方法;有抽象方法,类必须要是抽象类abstract class Application { //工厂方法类,返回的是一个接口类 abstract Product getProduct();}//子类继承了抽象类,子类必须实现抽象类里的方法class CreateProductA extends Application{ @Override Product getProduct() { //ProductA实现了Product接口,所以可以把创建的ProductA用Product接收 return new ProductA(); }}//子类继承了抽象类,子类必须实现抽象类里的方法class CreateProductB extends Application{ @Override Product getProduct() { return new ProductB(); }}
三.抽象工厂模式
Abstract Factory提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
应用场景:
程序需要处理不同序列的相关产品,但是你不希望它依赖于这些产品的具体类时,可是使用抽象工厂。
优点:
可以确信你从工厂得到的产品彼此是兼容的;
可以避免具体产品和客户端代码之间的紧密耦合;
符合单一职责原则;
符合开闭原则。
public class AbstractFactory { public static void main(String[] args) { //创建一个类,使用其实现的接口来接收 IDataBaseUtils iDataBaseUtils = new MysqlDataBaseUtils(); IConnection iConnection = iDataBaseUtils.getConnection(); iConnection.connection(); ICommand iCommand = iDataBaseUtils.getCommand(); iCommand.command(); }}interface IConnection{ void connection();}interface ICommand{ void command();}interface IDataBaseUtils{ //包含一组的工厂方法 IConnection getConnection(); ICommand getCommand();}class MysqlConnection implements IConnection{ @Override public void connection() { System.out.println("mysql connection"); }}class OracleConnection implements IConnection{ @Override public void connection() { System.out.println("oracle connection"); }}class MysqlCommand implements ICommand{ @Override public void command() { System.out.println("mysql command"); }}class OracleCommand implements ICommand{ @Override public void command() { System.out.println("oracle command"); }}class MysqlDataBaseUtils implements IDataBaseUtils{ @Override public IConnection getConnection() { return new MysqlConnection(); } @Override public ICommand getCommand() { return new MysqlCommand(); }}class OracleDataBaseUtils implements IDataBaseUtils{ @Override public IConnection getConnection() { return new OracleConnection(); } @Override public ICommand getCommand() { return new OracleCommand(); }}
四.建造者模式
将一个复杂对象的创建与他的标识分离,使得同样的构建过程可以创建不同的表示
应用场景:
需要生成的对象具有复杂的内部结构;
需要生成的对象内部属性本身相互依赖;
与不可变对象配合使用。
优点:
建造者独立,易扩展;
便于控制细节风险。
spring源码中的使用:
org.springframework.web.servlet.mvc.method.RequestMappingInfo;
org.springframework.beans.factory.support.BeanDefinitionBuilder;
public class BuilderTest { public static void main(String[] args) { ProductBulider productBulider = new DefaultCreateBuilderProduct(); Director director = new Director(productBulider); Product product = director.makeProduct("xxx", "1", "2", "3"); System.out.println(product.toString()); }}interface ProductBulider{ void buliderProductName(String productName); void buliderPart1(String part1); void buliderPart2(String part2); void buliderPart3(String part3); Product bulid();}class Director{ private ProductBulider productBulider; public Director(ProductBulider productBulider){ this.productBulider = productBulider; } public Product makeProduct(String productName, String part1, String part2, String part3){ productBulider.buliderProductName(productName); productBulider.buliderPart1(part1); productBulider.buliderPart2(part2); productBulider.buliderPart3(part3); Product product = productBulider.bulid(); return product; }}class DefaultCreateBuilderProduct implements ProductBulider{ private String productName; private String part1; private String part2; private String part3; @Override public void buliderProductName(String productName) { this.productName = productName; } @Override public void buliderPart1(String part1) { this.part1 = part1; } @Override public void buliderPart2(String part2) { this.part2 = part2; } @Override public void buliderPart3(String part3) { this.part3 = part3; } @Override public Product bulid() { return new Product( this.productName, this.part1, this.part2, this.part3); }}class Product{ private String productName; private String part1; private String part2; private String part3; public Product(){ } public Product(String productName, String part1, String part2, String part3) { this.productName = productName; this.part1 = part1; this.part2 = part2; this.part3 = part3; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getPart1() { return part1; } public void setPart1(String part1) { this.part1 = part1; } public String getPart2() { return part2; } public void setPart2(String part2) { this.part2 = part2; } public String getPart3() { return part3; } public void setPart3(String part3) { this.part3 = part3; } @Override public String toString() { return "Product{" + "productName='" + productName + '\'' + ", part1='" + part1 + '\'' + ", part2='" + part2 + '\'' + ", part3='" + part3 + '\'' + '}'; }}
简化版的构造模式(内部静态类)
public class BuilderTest { public static void main(String[] args) { Product product = new Product.ProductBuilder().productName("xxx").part1("1").part2("2").part3("3").bulid(); System.out.println(product.toString()); }}class Product{ private final String productName; private final String part1; private final String part2; private final String part3; static class ProductBuilder{ private String productName; private String part1; private String part2; private String part3; public ProductBuilder productName(String productName){ this.productName = productName; return this; } public ProductBuilder part1(String part1){ this.part1 = part1; return this; } public ProductBuilder part2(String part2){ this.part2 = part2; return this; } public ProductBuilder part3(String part3){ this.part3 = part3; return this; } Product bulid(){ return new Product(this.productName,this.part1,this.part2,this.part3); } } public Product(String productName, String part1, String part2, String part3) { this.productName = productName; this.part1 = part1; this.part2 = part2; this.part3 = part3; } @Override public String toString() { return "Product{" + "productName='" + productName + '\'' + ", part1='" + part1 + '\'' + ", part2='" + part2 + '\'' + ", part3='" + part3 + '\'' + '}'; }}
五.原型模式
原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
应用场景:
当代码不需要依赖于需要复制的对象的具体类时,请使用Prototype模式。
优点:
可以不耦合具体类的情况下克隆对象;
避免重复的初始化代码;
更方便的构建复杂对象。
Spring源码中的应用:
org.springframework.beans.factory.support.AbstractBeanDefinition
java.util.Arrays
public class PrototypeTest { public static void main(String[] args) throws CloneNotSupportedException { BaseInfo baseInfo = new BaseInfo("1111"); Product product = new Product("xx商品","part1","part2","part3",baseInfo); System.out.println(product.toString()); Product clone = product.clone(); System.out.println(clone.toString()); }}class BaseInfo implements Cloneable{ private String productId; public BaseInfo(String productId){ this.productId = productId; } @Override protected BaseInfo clone() throws CloneNotSupportedException{ BaseInfo baseInfo = (BaseInfo)super.clone(); return baseInfo; } @Override public String toString() { return super.hashCode()+" ] BaseInfo{" + "productId='" + productId + '\'' + '}'; }}class Product implements Cloneable{ private String productName; private String part1; private String part2; private String part3; private BaseInfo baseInfo; @Override protected Product clone() throws CloneNotSupportedException{ Product product = (Product)super.clone(); BaseInfo baseInfo = (BaseInfo)product.baseInfo.clone(); product.setBaseInfo(baseInfo); return product; } public Product(String productName, String part1, String part2, String part3,BaseInfo baseInfo) { this.productName = productName; this.part1 = part1; this.part2 = part2; this.part3 = part3; this.baseInfo = baseInfo; } @Override public String toString() { return super.hashCode()+"Product{" + "productName='" + productName + '\'' + ", part1='" + part1 + '\'' + ", part2='" + part2 + '\'' + ", part3='" + part3 + '\'' + ", baseInfo=" + baseInfo + '}'; } public BaseInfo getBaseInfo() { return baseInfo; } public void setBaseInfo(BaseInfo baseInfo) { this.baseInfo = baseInfo; }}
对一个对象的拷贝我们使实体实现Cloneable接口,重写它的clone方法;若是实体里面引入了其它的实体,例如上面的Product类中引入了BaseInfo类,若是BaseInfo类没有实现Cloneable接口,重写它的clone方法,则对BaseInfo的拷贝是浅拷贝(类的hashcode相同):
浅拷贝的问题时,修改其中一个的BaseInfo值时,另一个也会跟随着变化。
要达到深拷贝的程度,我们需要让引入的BaseInfo也实现Cloneable接口,重写它的clone方法,在Product类中调用clone方法时,调用下BaseInfo的clone来拷贝BaseInfo,此时是深拷贝(类的hashcode不相同):
六.享元模式
享元模式运用共享技术有效的支持大量细粒度的对象。
优点:
如果系统有大量类似的对象,可以节省大量的内存及CUP资源
Spring源码中的运用:
1.String、Integer、Long2.com.sun.org.apache.bcel.internal.generic.InstructionConstants
public class FlyWeightTest { public static void main(String[] args) { //创建多个marker对象,里面的参数BaseMessage相同时使用共享的方式 Marker marker1 = new Marker(new BigDecimal(10),new BigDecimal(30),BaseMessageFactory.getBaseMessage("xx","xxxtip")); Marker marker2 = new Marker(new BigDecimal(60),new BigDecimal(80),BaseMessageFactory.getBaseMessage("xx","xxxtip")); System.out.println(marker1.toString()); System.out.println(marker2.toString()); }}class Marker{ private BigDecimal lat; private BigDecimal lon; private BaseMessage baseMessage; public Marker(BigDecimal lat, BigDecimal lon, BaseMessage baseMessage) { this.lat = lat; this.lon = lon; this.baseMessage = baseMessage; } @Override public String toString() { return "Marker{" + "lat=" + lat + ", lon=" + lon + ", baseMessage=" + baseMessage.toString() + '}'; }}class BaseMessageFactory{ //使用map存放已经创建的对象,供其他线程创建时复用 private static Map map = new ConcurrentHashMap(); public static BaseMessage getBaseMessage(String title,String tip){ if(map.containsKey(title)){ return map.get(title); } BaseMessage baseMessage = new BaseMessage(title,tip); map.put(title,baseMessage); return baseMessage; }}class BaseMessage{ //字段使用final修饰,创建后不可改变值 private final String title; private final String tip; BaseMessage(String title, String tip) { System.out.println("create BaseMessage:"+title); this.title = title; this.tip = tip; } @Override public String toString() { return "BaseMessage{" + "title='" + title + '\'' + ", tip='" + tip + '\'' + '}'; }}
七.门面模式
门面模式为子系统中的一组接口提供一个一致的接口,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
应用场景:
当需要使用复杂子系统的有限但直接的接口时,请使用Facade模式。
当您想要将子系统组织成层时,请使用Facade。
优点:
简化客户端的调用。
源码中的经典应用:
1.org.apache.catalina.connector.RequestFacade
public class FacadeTest { public static void main(String[] args) { Client1 client1 = new Client1(); client1.pritlnSomething(); }}//客户端1class Client1{ Facate facate = new Facate(); public void pritlnSomething(){ //使用门面模式来统一调用 facate.printAll(); }}//门面模式,有facate来统一调用需要调用的方法class Facate{ SubMessage1 subMessage1 = new SubMessage1(); SubMessage2 subMessage2 = new SubMessage2(); SubMessage3 subMessage3 = new SubMessage3(); public void printAll(){ subMessage1.printSub1(); subMessage2.printSub2(); subMessage3.printSub3(); }}//子类class SubMessage1{ public void printSub1(){ System.out.println("printSub1..."); }}class SubMessage2{ public void printSub2(){ System.out.println("printSub2..."); }}class SubMessage3{ public void printSub3(){ System.out.println("printSub3..."); }}
八.适配器模式
适配器模式将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:
当你希望使用某些现有类,但其接口与你的其它代码不兼容时,请使用适配器类。
当你希望重用几个现有的子类,这些子类缺少一些不能添加到超类中的公共功能时,请使用该模式。
优点:
符合单一职责原则。
符合开闭原则。
JDK & Spring源码中的应用
JDK:1.java.util.Arrays # asList()2.java.util.Collections # list()Spring:1.org.springframework.context.event.GenericApplicationListenerAdapter
Object形式的适配器模式(注入类的方式):
public class AdapterObject { public static void main(String[] args) { Target target = new Adapter(new Adaptee()); System.out.println(target.adapter10v()); }}interface Target{ int adapter10v();}class Adapter implements Target{ private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public int adapter10v() { int i = adaptee.adapter220v(); //经过处理,输出10V的电压 int j = i/22; System.out.println(String.format("原电压 %d v 转化后电压 %d v",i,j)); return j; }}//提供220V电压的类class Adaptee{ public int adapter220v(){ return 220; }}
Class形式的适配器模式(继承类的方式):
public class AdapterClassDemo { public static void main(String[] args) { TargetClass targetClass = new AdapterClass(); System.out.println(targetClass.adapter10v()); }}interface TargetClass{ int adapter10v();}//使用类继承的方式class AdapterClass extends AdapteeClass implements TargetClass{ @Override public int adapter10v() { int i = adapter220v(); int j = i /22; System.out.println(String.format("原电压 %d v 转化后电压 %d v",i,j)); return j; }}class AdapteeClass{ public int adapter220v(){ return 220; }}
九.装饰者模式
装饰者模式Decorator在不改变原有对象的基础上,将功能附加到对象上。
应用场景:
扩展一个类的功能或给一个类添加附加功能。
优点:
不改变原有对象的情况下,给一个对象扩展功能。
使用不同的组合可以实现不同的效果。
符合开闭原则(对扩展开放,对修改关闭)。
经典案例:
1.javax.servlet.http.HttpServletRequest2.javax.servlet.http.HttpServletResponse
public class DecoratorTest { public static void main(String[] args) { //这是一开始既有的实现 //使用接口接收创建的类;创建具体类的实现作为它实现的接口,此接口是一个参数,即可在不改变原有对象的基础上,扩展附加一些属性 Component component = new CreateDecorator2(new CreateDecoator1(new CreateComponent())); component.operation(); }}interface Component{ void operation();}class CreateComponent implements Component{ @Override public void operation() { System.out.println("拍照"); }}//抽象类实现接口,可以不实现接口中的方法,当子类继承此抽象类时再对接口中的方法进行实现abstract class Decorator implements Component{ Component component; public Decorator(Component component){ this.component = component; }}//子类继承抽象父类,需要实现父类未实现的接口方法,需要调用父类的定义的构造函数class CreateDecoator1 extends Decorator{ //构造父类的构造方法 public CreateDecoator1(Component component) { super(component); } @Override public void operation() { System.out.println("美颜"); //调用创建CreateDecoator1类时传递到父抽象类Decorator的component component.operation(); }}class CreateDecorator2 extends Decorator{ public CreateDecorator2(Component component) { super(component); } @Override public void operation() { System.out.println("滤镜"); component.operation(); }}
十.策略模式
策略模式Stragety定义了算法族,分别封装起来,让它们直接可以互相替换,此模式的变化独立于算法的使用者。
应用场景:
业务代码需要根据场景不同,切换不同的实现逻辑。
代码中存在大量if - else判断。
优点:
符合开闭原则。
避免使用多重条件转移语句,例if - else 语句、switch语句。
可以提高算法的安全性和保密性。
经典案例:
1.java.util.Arrays # sort(T[] a, Comparator super T> c)2.org.springframework.beans.factory.support.InstantiationStrategy
public class StrategyTest { public static void main(String[] args) { Zombie zombie = new NormalZombie(new OneStepMove(),new OneAttack()); zombie.display(); zombie.move(); zombie.attack(); //根据传入的值,改变对应的方法调用输出的结果 zombie.setMoveable(new TenStepMove()); zombie.setAttackable(new TenAttack()); System.out.println("......进过策略模式传递对象改变后,调用同样的方法输出结果......"); zombie.move(); zombie.attack(); }}//移动的接口interface Moveable{ void move();}//攻击的接口interface Attackable{ void attack();}//定义一个抽象类abstract class Zombie { abstract void display(); Moveable moveable; Attackable attackable; abstract void move(); abstract void attack(); public Zombie(Moveable moveable, Attackable attackable) { this.moveable = moveable; this.attackable = attackable; } public void setMoveable(Moveable moveable) { this.moveable = moveable; } public void setAttackable(Attackable attackable) { this.attackable = attackable; }}//子类继承抽象类class NormalZombie extends Zombie{ public NormalZombie(Moveable moveable, Attackable attackable) { super(moveable, attackable); } @Override void display() { System.out.println("普通类"); } @Override void move() { moveable.move(); } @Override void attack() { attackable.attack(); }}//子类继承抽象类class StyleZombie extends Zombie{ public StyleZombie(Moveable moveable, Attackable attackable) { super(moveable, attackable); } @Override void display() { System.out.println("风格类......"); } @Override void move() { moveable.move(); } @Override void attack() { attackable.attack(); }}//定义类实现具体的移动方法class OneStepMove implements Moveable{ @Override public void move() { System.out.println("走一步"); }}class TenStepMove implements Moveable{ @Override public void move() { System.out.println("走十步"); }}class OneAttack implements Attackable{ @Override public void attack() { System.out.println("一次攻击"); }}class TenAttack implements Attackable{ @Override public void attack() { System.out.println("十次攻击"); }}
程序执行输出结果值:
十一.模板方法模式
模板方法模式Template Method定义一个操作的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,即可重定义改算法的某些特定步骤。
应用场景:
一次性实现一个算法不变的部分,并将可变部分留给子类实现。
各个子类中,公共部分被提取出来,集中到一个公共的父类中,避免代码重复。
优点:
提高代码复用性:将相同部分代码,放到抽象的父类中;
提供扩展性:将不同的代码,放到不同的子类中,通过对子类的扩展,增加新的行为;
符合开闭原则:通过父类调用子类的操作,通过对子类的扩展来增加新的行为。
Servlet Api & Spring源码:
//service中定义了doGet/doPost等方法,具体的实现由子类进行1.javax.servlet.http.HttpServlet #service()//handleRequest中定义了ModelAndView返回值,具体的实现由子类进行2.org.springframework.web.servlet.mvc.AbstractController #handleRequest()
public class TemplateTest { public static void main(String[] args) { AbstraceClass abstraceClass = new SubClass2(); abstraceClass.operator(); }}abstract class AbstraceClass{ //一般此方法是默认调用的方法 public void operator(){ System.out.println("准备"); System.out.println("处理"); //调用模板方法,此方法由子类实现 templateMethod(); } protected abstract void templateMethod();}class SubClass1 extends AbstraceClass{ //重写父类定义的抽象方法 @Override protected void templateMethod() { System.out.println("子类1实现的方法"); }}class SubClass2 extends AbstraceClass{ //重写父类定义的抽象方法 @Override protected void templateMethod() { System.out.println("子类2实现的方法"); }}
十二.观察者模式
观察者模式Observer定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者都会收到通知并更新。
应用场景:
当更改一个对象的状态可能需要更改其它对象,并且实际的对象集事先未知或动态更改时,请使用观察者模式。
优点:
符合开闭原则;
可以在运行时建立对象之间的关系。
JDK & Spring源码中的应用:
//定义了存放观察者的集合、添加、删除、通知观察者update的方法1.java.util.Observable //定义了onApplicationEvent通知方法,消息的发布在ApplicationEventMulticaster中,由multicastEvent方法获取getApplicationListeners()注册的监听集合,遍历此集合调用通知方法2.org.springframework.context.ApplicationListener
public class ObserverTest { public static void main(String[] args) { Subject subject = new Subject(); Observer subClass1 = new SubClass1(); Observer subClass2 = new SubClass2(); //把观察者对象添加进依赖的对象里面 subject.add(subClass1); subject.add(subClass2); subject.notityAll("数据有更新了..."); System.out.println("...移出某个需要通知的对象后..."); subject.remove(subClass1); subject.notityAll("数据再次更新了..."); }}class Subject { List list = new ArrayList(); //添加需要通知的对象 public void add(Observer observer){ if(!list.contains(observer)){ list.add(observer); } } //移出需要通知的对象 public void remove(Observer observer){ list.remove(observer); } //有变动,通知所有的监听对象 public void notityAll(Object object){ for(int i = 0;i < list.size();i++){ Observer observer = list.get(i); observer.notity(object); } }}interface Observer{ void notity(Object object);}class SubClass1 implements Observer{ @Override public void notity(Object object) { System.out.println("通知消息1:"+object); }}class SubClass2 implements Observer{ @Override public void notity(Object object) { System.out.println("通知消息2:"+object); }}
十三.责任链模式
责任链模式Chain Of Responsibility 为请求创建了一个接收者对象的链。
应用场景:
一个请求的处理需要多个对象当中的一个或几个协作处理。
优点:
请求的发送者和接受者解耦;
可以控制执行顺序;
符合开闭原则和单一职责原则。
经典案例:
//doFilter方法里面有一个FilterChain责任链1.javax.servlet.Filter #doFilter()2.javax.servlet.FilterChain #doFilter()//sentinel中进行流控、熔断的策略就是使用的责任链模式,使用8中插槽slot的方式判断是否达到某种限制,达到则抛出对应的异常,触发对应的控制3.com.alibaba.csp.sentinel.CtSph #lookProcessChain()
public class ChainOfResponsibilityTest { public static void main(String[] args) { //通过构建者模式创建request Request request = new Request.RequestBuilder().isLogin(false).isEnter(false).bulid(); //定义责任链,LoginHandler的next为EnterHandler,EnterHandler的next为null Handler handler = new LoginHandler(new EnterHandler(null)); boolean process = handler.process(request); if(process){ System.out.println("校验通过"); } else { System.out.println("校验不通过"); } }}//定义一个请求类class Request { private boolean isLogin; private boolean isEnter; public Request(boolean isLogin, boolean isEnter) { this.isLogin = isLogin; this.isEnter = isEnter; } //使用建造者模式创建 static class RequestBuilder{ private boolean isLogin; private boolean isEnter; RequestBuilder isLogin(boolean isLogin){ this.isLogin = isLogin; return this; } RequestBuilder isEnter(boolean isEnter){ this.isEnter = isEnter; return this; } Request bulid(){ Request request = new Request(isLogin,isEnter); return request; } } public boolean isLogin() { return isLogin; } public boolean isEnter() { return isEnter; }}//定义一个抽象父类abstract class Handler{ Handler next; abstract boolean process(Request request); public Handler(Handler next) { this.next = next; } public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; }}//定义子类class LoginHandler extends Handler{ //定义next的值 public LoginHandler(Handler next) { super(next); } @Override boolean process(Request request) { System.out.println("登陆校验"); if(request.isLogin()){ Handler next = getNext(); if(null == next){ return true; } if(next.process(request)){ return true; } } return false; }}//定义子类class EnterHandler extends Handler{ public EnterHandler(Handler next) { super(next); } @Override boolean process(Request request) { System.out.println("进入处理"); if(request.isEnter()){ Handler next = getNext(); if(null == next){ return true; } if(next.process(request)){ return true; } } return false; }}
十四.代理模式
代理模式proxy代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行前后,对操作进行增强处理。
应用场景:
日志的采集;
实现aop;
Rpc远程接口调用。
优点:
在不修改目标对象的前提下,能通过代理对象对目标对象进行扩
①静态代理
需要定义接口或者父类,代理对象和被代理对象需要实现相同的接口或者继承相同的父类,在代理对象中注入被代理类,通过代理对象调用真实的对象。
案例:
public class StaticProxyTest { public static void main(String[] args) { RentHouse rentHouse = new ProxyRent(new RealRent()); rentHouse.rent(); }}//定义一个接口类interface RentHouse{ void rent();}//定义一个真实类class RealRent implements RentHouse{ @Override public void rent() { System.out.println("房东租房"); }}//定义一个代理类,代理类和真实类需要实现相同的接口class ProxyRent implements RentHouse{ //代理类需要注入真实的被代理类 private RealRent realRent; public ProxyRent(RealRent realRent) { this.realRent = realRent; } @Override public void rent() { //在调用被代理类之前代理类可以添加一些其他的步骤 System.out.println("带客户看房"); //调用被代理类的方法 realRent.rent(); //在调用被代理类之后代理类可以添加一些其他的步骤 System.out.println("签订合同"); }}
②jdk动态代理
jdk动态代理涉及到java.lang.reflect.InvocationHandler和Proxy类,实现InvocationHandler接口后,重写invoke执行代理类的方法;使用Proxy的newProxyInstanse创建代理类,需要的参数为接口类,所以jdk动态代理需要使用接口类来接收,源码方法如下:
//jdk创建代理类需要使用interfaces接口作为参数 public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
案例:
public class JdkDynamicProxyTest { public static void main(String[] args) { //jdk1.8及之前的版本,设置保持jdk代理文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //jkd1.8之后的版本,设置保持jdk代理文件 //System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); JdkDynamicProxy proxy = new JdkDynamicProxy(new Cat("小猫")); Animal animal = (Animal) Proxy.newProxyInstance(proxy.getClass().getClassLoader(),new Class[]{Animal.class},proxy); animal.getUp(); animal.sleep(); proxy = new JdkDynamicProxy(new Student("学生")); Person person = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(),new Class[]{Person.class},proxy); person.getUp(); person.sleep(); }}//定义一个动物的接口interface Animal{ //起床的方法 void getUp(); //睡觉的方法 void sleep();}//定义一个人的接口interface Person{ //起床的方法 void getUp(); //睡觉的方法 void sleep();}//定义类实现接口class Cat implements Animal{ private String name; public Cat(String name) { this.name = name; } @Override public void getUp() { System.out.println("动物"+name+"起床"); } @Override public void sleep() { System.out.println("动物"+name+"睡觉"); }}//定义接口实现类class Student implements Person{ private String name; public Student(String name) { this.name = name; } @Override public void getUp() { System.out.println("人类"+name+"起床"); } @Override public void sleep() { System.out.println("人类"+name+"睡觉"); }}//jdk动态代理,通过实现InvocationHandler,重写invoke方法class JdkDynamicProxy implements InvocationHandler { //需要注入需要代理的类 private Object bean; public JdkDynamicProxy(Object bean) { this.bean = bean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //再调用代理类之前做一些前置处理 String name = method.getName(); if("getUp".equals(name)){ System.out.println("早上好"); } else if("sleep".equals(name)){ System.out.println("晚安"); } //执行被代理类的方法 return method.invoke(bean,args); }}
可以通过System.getProperties().put来设置生成代理类的class文件,可以在invoke调用真实类之前附加一些处理,需要使用接口来接收代理类,生成的代理类继承自Proxy,实现的接口为创建代理类时传入的接口类,里面有接口类定义的方法。
③Cglib动态代理
Cglib是一个机遇ASM字节码生成库,它允许在运行时对字节码进行修改和动态生成,cglib生成的是真实类的子类。主要使用到org.springframework.cglib.proxy.MethodInterceptor和Enhancer类,代理类实现MethodInterceptor接口,重写invoke调用代理类的方法,使用Enhancer字节码增强器来创界代理类。
案例:
public class CglibDynamicProxyTest { public static void main(String[] args) { //输出生成的代理类 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\aop"); CglibDynamicPorxy cglibDynamicPorxy = new CglibDynamicPorxy(new CatCglib("小猫")); CatCglib catCglib = (CatCglib) cglibDynamicPorxy.getProxy(); catCglib.getUp(); catCglib.sleep(); cglibDynamicPorxy = new CglibDynamicPorxy(new StudentCglib("学生")); StudentCglib studentCglib = (StudentCglib) cglibDynamicPorxy.getProxy(); studentCglib.getUp(); studentCglib.sleep(); }}//创建代理类class CglibDynamicPorxy implements MethodInterceptor { //注入需要代理的类 Object bean; public CglibDynamicPorxy(Object bean) { this.bean = bean; } //字节码增强器,为无接口的类创建代理 private Enhancer enhancer = new Enhancer(); public Object getProxy(){ //设置父类 enhancer.setSuperclass(bean.getClass()); //设置方法的回调 enhancer.setCallback(this); //创建代理-子类,继承自父类 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { String name = method.getName(); if("getUp".equals(name)){ System.out.println("早上好"); } else if("sleep".equals(name)){ System.out.println("晚上好"); } return method.invoke(bean,objects); }}//定义类实现接口class CatCglib { private String name; public CatCglib() { } public CatCglib(String name) { this.name = name; } public void getUp() { System.out.println("动物"+name+"起床"); } public void sleep() { System.out.println("动物"+name+"睡觉"); }}//定义类class StudentCglib{ private String name; public StudentCglib() { } public StudentCglib(String name) { this.name = name; } public void getUp() { System.out.println("人类"+name+"起床"); } public void sleep() { System.out.println("人类"+name+"睡觉"); }}
可以通过System.setProperty来设置生成代理类的class文件地址,可以在invoke调用真实类之前附加一些处理,需要使用类来接收代理类,生成的代理类继承自真实类,实现的接口为Factory,里面有类定义的方法,相当于把真实类clone克隆了一份作为代理子类,代理类中有clone方法。
十五.桥接模式
桥接模式Bridge将抽象和实现分离,使它们可以独立变化,它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
应用场景:
当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时;
当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时;
当一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性时。
优点:
抽象和实现分离,扩展能力强;
符合开闭原则;
符合合成复用原则。
public class BridgeTest { public static void main(String[] args) { //创建实现化角色 Animal animal = new Cat(); //实现化角色,通过创建抽象子类时,注入进去;通过创建的子类进行桥接,使其可以调用到实现化角色 Behavior behavior = new SubBehavior1(animal); behavior.wakeUp(); //也可以使用其他组合 behavior = new SubBehavior2(new Sheep()); behavior.wakeUp(); }}//定义接口-实现化角色interface Animal{ void operation();}//具体实现化角色class Cat implements Animal{ @Override public void operation() { System.out.println("小猫..."); }}//具体实现化角色class Sheep implements Animal{ @Override public void operation() { System.out.println("山羊"); }}//定义抽象化角色abstract class Behavior{ //注入实现化角色 Animal animal; public Behavior(Animal animal) { this.animal = animal; } abstract public void wakeUp();}//扩展抽象化角色--桥接class SubBehavior1 extends Behavior{ public SubBehavior1(Animal animal) { super(animal); } @Override public void wakeUp() { System.out.println("扩展抽象化1调用:wakeUp"); animal.operation(); }}//扩展抽象化角色--桥接class SubBehavior2 extends Behavior{ public SubBehavior2(Animal animal) { super(animal); } @Override public void wakeUp() { System.out.println("扩展抽象化2调用:wakeUp"); animal.operation(); }}
十六.组合模式
组合模式Composite有时候又叫做整体-部分模式,它是一种将对象组合成树状的层次结构模式,用来表示整体-部分的关系,是用户对单个对象和组合对象有一致的访问性,属于结构形设计模式。它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下:
应用场景:
在需要表示一个对象整体与部分的层次结构的场合;
要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。
优点:
组合模式使得客户端代码可以一致的处理当个对象和组合对象,无须关心自己处理的是单个对象还是组合对象,简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而改变源代码,满足开闭原则。
①透明方式
在该方式中,由于抽象构件声明了所有子类中的方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件中没有add()、remove()、getChild()方法,却要实现他们(空实现或抛异常),这样会带来一些安全性问题。
例如使用组合模式组织这样的数型图:
public class TransparentCompositeTest { public static void main(String[] args) { Component composite0 = new Composite(); Component composite1 = new Composite(); Component left1 = new Leaf("1"); Component left2 = new Leaf("2"); Component left3 = new Leaf("3"); composite0.add(left1); composite0.add(composite1); composite1.add(left2); composite1.add(left3); composite0.operate(); }}//抽象构件-定义子类需要的所有方法interface Component{ void add(Component component); void remove(Component component); Component getChild(int index); void operate();}//树叶构件-没有添加、删除、获取子类的方法实现class Leaf implements Component{ private String name; public Leaf(String name) { this.name = name; } @Override public void add(Component component) { } @Override public void remove(Component component) { } @Override public Component getChild(int index) { return null; } @Override public void operate() { System.out.println("叶子:"+name+"被访问"); }}//树枝构件-包含它下面的子类集合class Composite implements Component{ private List childList = new ArrayList(); @Override public void add(Component component) { childList.add(component); } @Override public void remove(Component component) { childList.remove(component); } @Override public Component getChild(int index) { return childList.get(index); } @Override public void operate() { for(Component component : childList){ component.operate(); } }}
②安全方式
在该方式中,将管理子构件的方法放到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了透明方式的安全性问题,但由于叶子和树枝有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
public class SafeCompositeTest { public static void main(String[] args) { CompositeSafe compositeSafe0 = new CompositeSafe(); CompositeSafe compositeSafe1 = new CompositeSafe(); ComponentSafe leaf1 = new LeafSafe("1"); ComponentSafe leaf2 = new LeafSafe("2"); ComponentSafe leaf3 = new LeafSafe("3"); compositeSafe0.add(leaf1); compositeSafe0.add(compositeSafe1); compositeSafe1.add(leaf2); compositeSafe1.add(leaf3); compositeSafe0.operate(); }}//抽象构件-只定义公共的方法interface ComponentSafe{ void operate();}//树叶构件class LeafSafe implements ComponentSafe{ private String name; public LeafSafe(String name) { this.name = name; } @Override public void operate() { System.out.println("叶子:"+name+"被调用"); }}//树枝构建class CompositeSafe implements ComponentSafe{ private List childList = new ArrayList(); public void add(ComponentSafe componentSafe){ childList.add(componentSafe); } public void remove(ComponentSafe componentSafe){ childList.remove(componentSafe); } public ComponentSafe getChild(int index){ return childList.get(index); } @Override public void operate() { for(ComponentSafe componentSafe : childList){ componentSafe.operate(); } }}
十七.命令模式
命令模式Command将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加和管理。
应用场景:
请求调用者需要和请求接收者解耦时,命令模式可以使调用者和接收者不直接交互;
系统随机请求命令或经常增加、删除命令时,命令模式可以方便的实现这些功能;
当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
优点:
通过引入中间件(抽象接口)降低系统的耦合度;
扩展性良好,增加或删除命令非常方便,采用命令模式增加或删除命令不会影响其他类,且满足开闭原则;
可以实现宏命令,命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令;
方便实现Undo和Redo操作,命令模式可以与备忘录模式结合使用,实现命令的撤销和恢复;
可以在现有命令的基础上,增加而外功能,例如日志记录。
public class CommandTest { public static void main(String[] args) { //创建翻上一个视频的命令对象 Command upVideoCommand = new UpVideoCommand(); //创建关闭电视机的命令对象 Command closeVideoCommand = new CloseVideoCommand(); //创建调用者 ControllerInvoker controllerInvoker = new ControllerInvoker(); //把可以调用的命令设置到调用者中 controllerInvoker.setUpVideoCommand(upVideoCommand); controllerInvoker.setCloseVideoCommand(closeVideoCommand); //调用者调用方法-翻上一个视频 controllerInvoker.upVideo(); //调用者调用方法-关闭电视机 controllerInvoker.closeVideo(); }}//抽象命令类interface Command{ void execute();}//具体命令类-翻上一个视频操作class UpVideoCommand implements Command{ //注入此命令的接收者 private VideoTopReceiver videoTopReceiver; public UpVideoCommand() { videoTopReceiver = new VideoTopReceiver(); } @Override public void execute() { System.out.println("按了翻上一个视频的按钮"); //给接收者发送命令 videoTopReceiver.upVideo(); }}//具体命令类-关闭电视机class CloseVideoCommand implements Command{ //注入此命令的接收者 private VideoReceiver videoReceiver; public CloseVideoCommand() { videoReceiver = new VideoReceiver(); } @Override public void execute() { System.out.println("按了关机的按钮"); //给接收者发送命令 videoReceiver.closeVideo(); }}//命令实现者-电视机顶盒class VideoTopReceiver { public void upVideo(){ System.out.println("收到命令,翻上一个台"); }}//命令实现者-电视机class VideoReceiver{ public void closeVideo(){ System.out.println("收到命令,电视机关机"); }}//调用者-遥控器,设置了可以调用的命令class ControllerInvoker{ //定义好有哪些命令可以调用 private Command upVideoCommand,closeVideoCommand; public void setUpVideoCommand(Command upVideoCommand) { this.upVideoCommand = upVideoCommand; } public void setCloseVideoCommand(Command closeVideoCommand) { this.closeVideoCommand = closeVideoCommand; } //当按下调用上一个节目的按钮执行的方法 public void upVideo(){ upVideoCommand.execute(); } //当按电视关机的按钮执行的方法 public void closeVideo(){ closeVideoCommand.execute(); }}
十八.状态模式
状态模式State对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
应用场景:
1.当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式;
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
优点:
结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分隔开来,满足单一职责原则;
将状态转换化,减少对象间的相互依赖,将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖;
状态类职责明确,有利于程序的扩展,通过定义新的子类很容易的增加新的状态和转换。
例如:线程的状态转换
public class StateTest { public static void main(String[] args) { ThreadContext threadContext = new ThreadContext(); //启动线程,方法执行前状态:新建,执行后状态:变为就绪 threadContext.start(); //获取到cpu资源,方法执行前状态:就绪,执行后状态:变为运行 threadContext.getCpu(); //中断线程,方法执行前状态:运行,执行后状态:变为阻塞 threadContext.suspend(); //恢复线程,方法执行前状态:阻塞,执行后状态:变为就绪 threadContext.resume(); //获取到cpu资源,方法执行前状态:就绪,执行后状态:变为运行 threadContext.getCpu(); //停止线程 ,方法执行前状态:运行,执行后状态:变为停止 threadContext.stop(); }}//环境类-存放全局的状态class ThreadContext{ //环境类里面放一个记录状态的类,通过该变此对象的值,来改变环境的状态 ThreadState state; //创建环境类的时候,先默认给一个初始的state状态 public ThreadContext() { state = new New(); } //程序运行过程中获取状态 public ThreadState getState() { return state; } //程序运行过程中,重新设置状态值 public void setState(ThreadState state) { this.state = state; } //调用启动方法-状态为新建的时候 public void start(){ //把state抽象类强转成具体的子类,调用子类的方法,并把环境类传递过去,这样可以在此方法调用后改变state状态值 ((New)state).start(this); } //调用获取cpu资源-状态为就绪的时候 public void getCpu(){ //把state抽象类强转成具体的子类,调用子类的方法,并把环境类传递过去,这样可以在此方法调用后改变state状态值 ((Runnable)state).getCpu(this); } //调用线程中断-状态为运行的时候 public void suspend(){ //把state抽象类强转成具体的子类,调用子类的方法,并把环境类传递过去,这样可以在此方法调用后改变state状态值 ((Running)state).suspend(this); } //调用线程结束-状态为运行的时候 public void stop(){ //把state抽象类强转成具体的子类,调用子类的方法,并把环境类传递过去,这样可以在此方法调用后改变state状态值 ((Running)state).stop(this); } //调用线程结束-状态为阻塞的时候 public void resume(){ //把state抽象类强转成具体的子类,调用子类的方法,并把环境类传递过去,这样可以在此方法调用后改变state状态值 ((Blocked)state).resume(this); }}//抽象状态类abstract class ThreadState{ String stateName;}//具体状态类-线程新建状态,可以调用启动方法class New extends ThreadState{ public New(){ stateName = "新建状态"; System.out.println("线程创建."); } public void start(ThreadContext threadContext){ System.out.println("调用线程启动方法start()"); if("新建状态".equals(stateName)){ //重新设置环境类中state状态变量的值 threadContext.setState(new Runnable()); } else { System.out.println("线程没有处于新建状态,不允许启动线程"); } }}//具体实现类-线程就绪状态,可以调用获取cpu资源的方法class Runnable extends ThreadState{ public Runnable() { stateName = "就绪状态"; System.out.println("线程处于就绪状态"); } public void getCpu(ThreadContext threadContext){ System.out.println("调用线程获取资源方法getCpu()"); if("就绪状态".equals(stateName)){ //重新设置环境类中state状态变量的值 threadContext.setState(new Running()); } else { System.out.println("线程没有处于就绪状态,不允许获取资源"); } }}//具体实现类-线程运行状态,可以调用中断、结束线程的方法class Running extends ThreadState{ public Running() { stateName = "运行状态"; System.out.println("线程处于运行状态"); } public void suspend(ThreadContext threadContext){ System.out.println("调用线程中断方法suspend()"); if("运行状态".equals(stateName)){ //重新设置环境类中state状态变量的值 threadContext.setState(new Blocked()); } else { System.out.println("线程没有处于运行状态,不允许中断"); } } public void stop(ThreadContext threadContext){ System.out.println("调用线程结束方法stop()"); if("运行状态".equals(stateName)){ //重新设置环境类中state状态变量的值 threadContext.setState(new Stop()); } else { System.out.println("线程没有处于运行状态,不允许结束"); } }}//具体实现类-线程阻塞状态,可以调用恢复线程的方法class Blocked extends ThreadState{ public Blocked() { stateName = "阻塞状态"; System.out.println("线程处于阻塞状态"); } public void resume(ThreadContext threadContext){ System.out.println("调用线程恢复方法resume()"); if("阻塞状态".equals(stateName)){ //重新设置环境类中state状态变量的值 threadContext.setState(new Runnable()); } else { System.out.println("线程没有处于阻塞状态,不允许恢复"); } }}//具体实现类-线程结束状态class Stop extends ThreadState{ public Stop() { stateName = "结束状态"; System.out.println("线程处于结束状态"); }}
十九.中介者模式
中介者模式Mediator定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立的改变他们之间的交互。
应用场景:
当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时;
当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
优点:
类之间各司其职,符合迪米特法则;
降低了对象之间的耦合性,使得对象易于独立的被复用;
将对象之间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
例如:中介者结构图的实现
public class MediatorTest { public static void main(String[] args) { //创建中介者 Mediator mediator = new ConcreteMediator(); //创建同事类 Colleague concreteColleague1 = new ConcreteColleague1(); Colleague concreteColleague2 = new ConcreteColleague2(); //把同事类添加到中介者中 mediator.register(concreteColleague1); mediator.register(concreteColleague2); //同事类发送消息 concreteColleague1.send(); concreteColleague2.send(); }}//抽象中介者abstract class Mediator{ //同事类添加到中介者中 abstract void register(Colleague colleague); //转发消息 abstract void relay(Colleague colleague);}//抽象同事类abstract class Colleague{ //记录此同事类关联的中介者 Mediator mediator; //接收消息 abstract void receive(); //发送消息 abstract void send(); //设置此同事类关联的中介者 public void setMediator(Mediator mediator) { this.mediator = mediator; }}//具体中介者class ConcreteMediator extends Mediator{ //记录注册在此中介者中的同事集合 private List list = new ArrayList(); @Override void register(Colleague colleague) { if(!list.contains(colleague)){ //把同事类添加到它关联的中介者中 list.add(colleague); //给此同事类设置它关联的中介者 colleague.setMediator(this); } } @Override void relay(Colleague c) { for(Colleague colleague : list){ //转发的同事,不包含发消息的同事 if(!c.equals(colleague)){ colleague.receive(); } } }}//具体同事类class ConcreteColleague1 extends Colleague{ @Override void receive() { System.out.println("具体同事类1收到消息"); } @Override void send() { System.out.println("具体同事类1发送消息"); //通过此同事类关联的中介者进行消息转发(不包含转发给自己) mediator.relay(this); }}//具体同事类class ConcreteColleague2 extends Colleague{ @Override void receive() { System.out.println("具体同事类2收到消息"); } @Override void send() { System.out.println("具体同事类2发送消息"); //通过此同事类关联的中介者进行消息转发(不包含转发给自己) mediator.relay(this); }
二十.迭代器模式
迭代器模式Iterator提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
应用场景:
当需要为聚合对象提供多种遍历方式时;
当需要为遍历不同的聚合结构提供一个统一的接口时;
当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
优点:
访问一个聚合对象的内容而无须暴露它的内部表示;
遍历任务交由迭代器完成,这简化了聚合类;
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历;
增加新的聚合类和迭代器类都很方便,无须修改原有代码;
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
public class IteratorTest { public static void main(String[] args) { //创建聚合类 Aggegate concreteAggegate = new ConcreteAggegate(); //向聚合类中添加对象 concreteAggegate.add("苹果"); concreteAggegate.add("橘子"); concreteAggegate.add("香蕉"); concreteAggegate.add("芒果"); //把聚合类中的集合对象作为参数传递到迭代器中,创建迭代器 Iterator iterator = concreteAggegate.getIterator(); //遍历数据 while(iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println("遍历结束"); System.out.println(iterator.first()); System.out.println(iterator.hasNext()); System.out.println(iterator.next()); System.out.println(iterator.previous()); System.out.println(iterator.last()); System.out.println(iterator.hasNext()); }}//聚合抽象类interface Aggegate{ //添加元素到集合中 void add(Object object); //从集合中移出元素 void remove(Object object); //获得迭代器 Iterator getIterator();}//抽象迭代器类interface Iterator{ //获取第一个元素 Object first(); //获取下一个元素 Object next(); //获取前一个元素 Object previous(); //获取最后一个元素 Object last(); //是否还有下一个元素 boolean hasNext();}//具体聚合类class ConcreteAggegate implements Aggegate{ //定义一个集合,用于存放加入到聚合类中的对象 private List
二十一.访问者模式
访问者模式visitor将作用于某种数据结构中的各元素的操作分离出来分装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式,它将对数据的操作与数据结构进行分离。
应用场景:
对象结构相对稳定,但其操作算法经常变化的程序;
对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
优点:
扩展性好,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能;
复用性好,可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度;
灵活性好,访问者模式将数据结构和作用于结构上的操作解耦,是的操作集合可相对自由的演化而不影响系统的数据结构;
符合单一职责原则,访问者模式把相关的行为封装在一起,构成一个访问者,使每个访问者的功能都比较单一。
public class VisitorTest { public static void main(String[] args) { //创建结构对象 ObjectStructure objectStructure = new ObjectStructure(); //添加可以访问的元素 ConcreteElements1 concreteElements1 = new ConcreteElements1(); ConcreteElements2 concreteElements2 = new ConcreteElements2(); objectStructure.add(concreteElements1); objectStructure.add(concreteElements2); //创建访问对象 Visitor visitor = new ConcreteVisitor1(); //结构对象接受visitor访问者的访问,遍历结构对象中存的所有元素接受访问 objectStructure.accept(visitor); }}//抽象访问者interface Visitor{ //访问哪个元素,元素作为参数 void visit(ConcreteElements1 elements); void visit(ConcreteElements2 elements);}//抽象元素interface Elements{ //接收谁的访问,访问者作为参数传递进来 void accept(Visitor vistor);}//具体访问者class ConcreteVisitor1 implements Visitor{ @Override public void visit(ConcreteElements1 elements) { System.out.print("具体访问者1访问-》"); elements.operator1(); } @Override public void visit(ConcreteElements2 elements) { System.out.print("具体访问者1访问-》"); elements.operator2(); }}//具体元素class ConcreteElements1 implements Elements{ //元素接收访问 @Override public void accept(Visitor visitor) { //当前元素接受visitor的访问,调用visitor的访问方法 visitor.visit(this); } public void operator1(){ System.out.println("元素1"); }}//具体元素class ConcreteElements2 implements Elements{ //元素接收访问 @Override public void accept(Visitor visitor) { //当前元素接受visitor的访问 visitor.visit(this); } public void operator2(){ System.out.println("元素2"); }}//对象结构class ObjectStructure{ //存放有哪些元素可以访问 List list = new ArrayList(); //添加元素到集合 public void add(Elements elements){ list.add(elements); } //接受访问-接受谁的方法,作为参数传递进来 public void accept(Visitor visitor){ Iterator iterator = list.iterator(); while(iterator.hasNext()){ //让元素接受某个访问者的访问 iterator.next().accept(visitor); } }}
二十二.备忘录模式
备忘录模式Memento在不破坏封装性的前提下,捕获一个对象的内部状态,并在改对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态,该模式又叫快照模式。
应用场景:
需要保存与恢复数据的场景,如玩游戏时的中间结果存档功能;
需要提供一个可回滚操作的场景,如Word、记事本、Photoshop、Eclipse等软件在编辑时按Ctrl+Z组合键,还有数据库中事务操作。
优点:
提供了一种可以恢复状态的机制,当用户需要时能够比较方便的将数据恢复到某个历史状态;
实现了内部状态的封装,除了创建它的发起人之外,其它对象都不能够访问这些状态信息;
简化了发起人类,发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
public class MementoTest { public static void main(String[] args) { //创建管理者 Caretaker caretaker = new Caretaker(); //创建发起者 Originator originator = new Originator(); originator.setState("0"); //创建备份 Memento memento = originator.createMemento(); //备份添加到管理者中 caretaker.push(memento); originator.setState("1"); //备份添加到管理者中 caretaker.push(originator.createMemento()); originator.setState("2"); System.out.println("当前状态值:"+originator.getState()); //第一次还原 originator.restoreMemento(caretaker.pop()); System.out.println("第一次还原状态值:"+originator.getState()); //第二次还原 originator.restoreMemento(caretaker.pop()); System.out.println("第二次还原状态值:"+originator.getState()); }}//备忘录实体-需要记录发起人存放的所有字符值class Memento{ //字段,与发起人实体中相同的字段 private String state; public Memento(String state) { this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; }}//发起人角色class Originator{ private String state; //创建备份录 public Memento createMemento(){ //把需要备份的字段都传递到备份类实体中进行备份 return new Memento(state); } //恢复备份-通过某个备份文件还原当前发起人的数据 public void restoreMemento(Memento memento){ //把备份类记录的值还原到发起人类对应的字段中 this.state = memento.getState(); } public String getState() { return state; } public void setState(String state) { this.state = state; }}//管理者-记录着备份的对象class Caretaker{ //使用栈记录备份的记录,栈的特性:先进后出,符合还原操作 private Stack stack = new Stack(); //添加备份到栈 public void push(Memento memento) { stack.push(memento); } //还原时,从栈中拿出备份的记录 public Memento pop(){ return stack.pop(); }}
二十三.解释器模式
解释器模式Interpreter给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例,这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
应用场景:
当语言的文法比较简单,且执行效率不是关键问题时;
当问题重复出现,且可以用一种简单的语言来进行表达时;
当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。
优点:
扩展性好,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法;
容易实现,在语法树中的每个表达式节点类都是相似的,所以实现其文法较为简单。
public class InterpreterTest { public static void main(String[] args) { Context context = new Context(); context.checkMes("军人的子女"); context.checkMes("军人的妻子"); context.checkMes("工人的子女"); context.checkMes("警察的父母"); context.checkMes("警察的朋友"); }}//抽象表达式interface AbstractExpression{ //接收某个输入的信息 boolean interprete(String info);}//终结者表达式-名词、代词class TerminalExpression implements AbstractExpression{ //定义集合存放允许的词 private List list = new ArrayList(); //创建对象时,需要给出允许的词 public TerminalExpression(String[] arrStr) { for(int i = 0;i < arrStr.length;i++){ list.add(arrStr[i]); } } @Override public boolean interprete(String info) { //包含这个此,返回true if(list.contains(info)){ return true; } return false; }}//非终结者表达式-谓语(动词)class NonTerminalExpression implements AbstractExpression{ //记录终结者表达式的对象 private AbstractExpression jobExpression; private AbstractExpression persionExpression; public NonTerminalExpression(AbstractExpression jobExpression, AbstractExpression persionExpression) { this.jobExpression = jobExpression; this.persionExpression = persionExpression; } @Override public boolean interprete(String info) { //非终结者进行分词 String[] arr = info.split("的"); //判断是否包含 return jobExpression.interprete(arr[0]) && persionExpression.interprete(arr[1]); }}//环境角色class Context{ //定义允许的此 String[] jobArr = {"军人","警察"}; String[] persionArr = {"父母","子女"}; //定义一个非终结表达式对象 AbstractExpression nonTerminalExpression; public Context() { //创建终结者表达式对象 TerminalExpression job = new TerminalExpression(jobArr); TerminalExpression persion = new TerminalExpression(persionArr); nonTerminalExpression = new NonTerminalExpression(job, persion); } //输入一个字符判断是否符合表达式 public void checkMes(String info){ boolean interprete = nonTerminalExpression.interprete(info); if(interprete){ System.out.println(info+"可以享受优先的政策"); } else { System.out.println(info+"正常排队"); } }}
来源地址:https://blog.csdn.net/ZHANGLIZENG/article/details/127511376