☀️一张思维图带大家了解Java常见设计模式☀️《❤️记得收藏❤️》
目录
🏳️🌈开讲啦!!!!🏳️🌈
🏳️🌈1、前言
:steam_locomotive:2、单例模式
:railway_car:2.1、单例模式的定义与特点
:bullettrain_side:2.2、单例模式的结构与实现
:bullettrain_front:2.3、单例模式的结构
:train2:2.4、单例模式的实现
:metro:3、原型模式
:light_rail:3.1、原型模式的定义与特点
:station:3.2、原型模式的结构与实现
:tram:3.3、原型模式的应用实例
:monorail:4、工厂方法模式
:mountain_railway:4.1、工厂方法模式的定义与特点
:train:4.2、工厂方法模式的结构与实现
:bus:5、抽象工厂模式
:oncoming_bus:5.1、抽象工厂模式的定义与特点
:trolleybus:5.2、抽象工厂模式的结构与实现
:minibus:6、设计代理模式
:ambulance:6.1、代理模式的定义与特点
:fire_engine:6.2、代理模式的结构与实现
:police_car:6.3、代理模式的应用实例
:oncoming_police_car:7、动态代理
:taxi:7.1、JDK动态代理
:oncoming_taxi:7.2、cglib动态代理
:car:8、装饰模式
:oncoming_automobile:8.1、装饰模式的定义与特点
:blue_car:8.2、装饰模式的结构与实现
:truck:8.3装饰模式的应用实例
:articulated_lorry:8.3、装饰模式的应用场景
:tractor:9、适配器模式
:racing_car:9.1、适配器模式的定义与特点
:motorcycle:9.2、适配器模式的结构与实现
:construction:9.3、适配器模式的应用场景
:stop_sign:10、观察者模式
:vertical_traffic_light:10.1、观察者模式的定义与特点
:traffic_light:10.2、观察者模式的结构与实现
:rotating_light:10.3、观察者模式的应用实例
:fuelpump:10.4、观察者模式的应用场景
🏳️🌈关注苏州程序大白,持续更新技术分享。谢谢大家支持🏳️🌈
目录
🏳️🌈开讲啦!!!!🏳️🌈
🏳️🌈1、前言
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解,本文主要列举了常用的几种设计模式。
🚂2、单例模式
🚃2.1、单例模式的定义与特点
单例(Singleton)模式的定义:
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
单例模式有 3 个特点:
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点;
🚄2.2、单例模式的结构与实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
🚅2.3、单例模式的结构
单例模式的主要角色如下:
单例类:包含一个实例且能自行创建这个实例的类。
访问类:使用单例的类。
🚆2.4、单例模式的实现
Singleton 模式通常有两种实现形式。
1 、懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:
public class LazySingleton { private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步 private LazySingleton(){} //private 避免类在外部被实例化 public static synchronized LazySingleton getInstance() { //getInstance 方法前加同步 if(instance==null) { instance=new LazySingleton(); } return instance; } }
注意:如果编写的是多线程程序,则必须有上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。这两个关键字保证了线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
2 、饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton { private static final HungrySingleton instance=new HungrySingleton(); private HungrySingleton(){} public static HungrySingleton getInstance() { return instance; } }
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。
单例模式的应用实例
用懒汉式单例模式模拟产生董事长对象。
public class SingletonLazy { public static void main(String[] args) { President zt1=President.getInstance(); zt1.getName(); //输出董事长名字 President zt2=President.getInstance(); zt2.getName(); //输出董事长名字 if(zt1==zt2) { System.out.println("他们是同一人!"); } else { System.out.println("他们不是同一人!"); } } } class President { private static volatile President instance=null; //保证instance在所有线程中同步 //private避免类在外部被实例化 private President() { System.out.println("产生一个董事长!"); } public static synchronized President getInstance() { //在getInstance方法上加同步 if(instance==null) { instance=new President(); } else { System.out.println("已经有一个董事长,不能产生新董事长!"); } return instance; } public void getName() { System.out.println("我是董事长:xxx。"); } }
运行结果:
🚇3、原型模式
🚈3.1、原型模式的定义与特点
原型(Prototype)模式的定义如下:
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
🚉3.2、原型模式的结构与实现
由于 Java提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
1、模式的结构:
原型模式包含以下主要角色:
抽象原型类:规定了具体原型对象必须实现的接口。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
2、 模式的实现
原型模式的克隆分为浅克隆和深克隆,Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。其代码如下:
//具体原型类 class Realizetype implements Cloneable { Realizetype() { System.out.println("具体原型创建成功!"); } public Object clone() throws CloneNotSupportedException { System.out.println("具体原型复制成功!"); return (Realizetype)super.clone(); } } //原型模式的测试类 public class PrototypeTest { public static void main(String[] args)throws CloneNotSupportedException { Realizetype obj1=new Realizetype(); Realizetype obj2=(Realizetype)obj1.clone(); System.out.println("obj1==obj2?"+(obj1==obj2)); } }
运行结果:
🚊3.3、原型模式的应用实例
用原型模式生成“三好学生”奖状。
public class ProtoTypeCitation { public static void main(String[] args) throws CloneNotSupportedException { citation obj1=new citation("小红","同学:在2020学年第一学期中表现优秀,被评为三好学生。","信息学院"); obj1.display(); citation obj2=(citation) obj1.clone(); obj2.setName("小明"); obj2.display(); } } //奖状类 class citation implements Cloneable { String name; String info; String college; citation(String name,String info,String college) { this.name=name; this.info=info; this.college=college; System.out.println("奖状创建成功!"); } void setName(String name) { this.name=name; } String getName() { return(this.name); } void display() { System.out.println(name+info+college); } public Object clone() throws CloneNotSupportedException { System.out.println("奖状拷贝成功!"); return (citation)super.clone(); } }
运行结果:
🚝4、工厂方法模式
🚞4.1、工厂方法模式的定义与特点
工厂方法(FactoryMethod)模式的定义:
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点,被创建的对象称为“产品”,把创建产品的对象称为“工厂。
工厂方法模式的主要优点和缺点有:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
其缺点是:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
🚋4.2、工厂方法模式的结构与实现
工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。
1、模式的结构:
工厂方法模式的主要角色如下:
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
2、模式的实现
/** * 冰箱接口 * */ public interface IRefrigerator { //获取品牌名 String getBrandName(); //获取价格 double getPrice(); } /** * 冰箱工厂接口 */ public interface IRefrigeratorFactory { IRefrigerator getIRefrigerator(); } /** * 海尔冰箱 */ public class HaiErRefrigerator implements IRefrigerator { @Override public String getBrandName() { return "海尔冰箱"; } @Override public double getPrice() { return 5999; } } /** * 美的冰箱 */ public class MeiDiRefrigerator implements IRefrigerator { @Override public String getBrandName() { return "美的冰箱"; } @Override public double getPrice() { return 2999;} } /** * 格力冰箱 */ public class GeLiRefrigerator implements IRefrigerator { @Override public String getBrandName() { return "格力冰箱"; } @Override public double getPrice() { return 3999; } } /** * 海尔冰箱工厂 */ public class HaiErRefrigeratorFactory implements IRefrigeratorFactory { @Override public IRefrigerator getIRefrigerator() { return new HaiErRefrigerator(); } } /** * 美的冰箱工厂 */ public class MeiDiRefrigeratorFactory implements IRefrigeratorFactory { @Override public IRefrigerator getIRefrigerator() { return new MeiDiRefrigerator(); } } /** * 格力冰箱工厂 */ public class GeLiRefrigeratorFactory implements IRefrigeratorFactory { @Override public IRefrigerator getIRefrigerator() { return new GeLiRefrigerator(); } } /** * 测试类 */ public class Test { public static void main(String[] args) { IRefrigeratorFactory refrigeratorFactory = new HaiErRefrigeratorFactory(); IRefrigeratorFactory refrigeratorFactory2 = new GeLiRefrigeratorFactory(); IRefrigeratorFactory refrigeratorFactory3 = new MeiDiRefrigeratorFactory(); IRefrigerator refrigerator = refrigeratorFactory.getIRefrigerator(); System.out.println("您购买了:" + refrigerator.getBrandName() + ",您需要支付:" + refrigerator.getPrice()); } }
运行结果:
🚌5、抽象工厂模式
🚍5.1、抽象工厂模式的定义与特点
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件。
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
🚎5.2、抽象工厂模式的结构与实现
抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
1、抽象工厂模式的结构
抽象工厂模式的主要角色如下:
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
2、抽象工厂模式的实现
1、抽象工厂:提供了产品的生成方法。
interface AbstractFactory { public Product1 newProduct1(); public Product2 newProduct2(); }
2、具体工厂:实现了产品的生成方法。
class ConcreteFactory1 implements AbstractFactory { public Product1 newProduct1() { System.out.println("具体工厂 1 生成-->具体产品 11..."); return new ConcreteProduct11(); } public Product2 newProduct2() { System.out.println("具体工厂 1 生成-->具体产品 21..."); return new ConcreteProduct21(); } }
3、抽象工厂模式的应用实例
/** * 为形状创建一个接口 */ public interface Shape { void draw(); } ** * 为颜色创建一个接口 */ public interface Color { void fill(); } /** * 创建实现接口的实体类 */ public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square implements Shape { @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } /** * 创建实现接口的实体类 */ public class Red implements Color { @Override public void fill() { System.out.println("Inside Red::fill() method."); } } public class Green implements Color { @Override public void fill() { System.out.println("Inside Green::fill() method."); } } public class Blue implements Color { @Override public void fill() { System.out.println("Inside Blue::fill() method."); } } /** * 为 Color 和 Shape 对象创建抽象类来获取工厂 */ interface AbstractFactory { public Color getColor(String color); public Shape getShape(String shape) ; } /** * 创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象 */ public class ShapeFactory extends AbstractFactory { @Override public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } else if(shapeType.equalsIgnoreCase("SQUARE")) { return new Square(); } return null; } @Override public Color getColor(String color) { return null; } } /** * 创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂 */ public class FactoryProducer { public static AbstractFactory getFactory(String choice){ if(choice.equalsIgnoreCase("SHAPE")){ return new ShapeFactory(); } else if(choice.equalsIgnoreCase("COLOR")){ return new ColorFactory(); } return null; } } public class AbstractFactoryPatternDemo { public static void main(String[] args) { //获取形状工厂 AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE"); //获取形状为 Circle 的对象 Shape shape1 = shapeFactory.getShape("CIRCLE"); //调用 Circle 的 draw 方法 shape1.draw(); //获取形状为 Rectangle 的对象 Shape shape2 = shapeFactory.getShape("RECTANGLE"); //调用 Rectangle 的 draw 方法 shape2.draw(); //获取形状为 Square 的对象 Shape shape3 = shapeFactory.getShape("SQUARE"); //调用 Square 的 draw 方法 shape3.draw(); //获取颜色工厂 AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR"); //获取颜色为 Red 的对象 Color color1 = colorFactory.getColor("RED"); //调用 Red 的 fill 方法 color1.fill(); //获取颜色为 Green 的对象 Color color2 = colorFactory.getColor("Green"); //调用 Green 的 fill 方法 color2.fill(); //获取颜色为 Blue 的对象 Color color3 = colorFactory.getColor("BLUE"); //调用 Blue 的 fill 方法 color3.fill(); } }
运行结果:
Inside Circle::draw() method. Inside Rectangle::draw() method. Inside Square::draw() method. Inside Red::fill() method. Inside Green::fill() method. Inside Blue::fill() method.
🚐6、设计代理模式
🚑6.1、代理模式的定义与特点
代理模式的定义:
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
其主要缺点是:
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
🚒6.2、代理模式的结构与实现
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
1、模式的结构
代理模式的主要角色如下:
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能
2、模式的实现
代理模式的实现代码如下:
//抽象主题 interface Subject { void Request(); } //真实主题 class RealSubject implements Subject { public void Request() { System.out.println("访问真实主题方法..."); } } //代理 class Proxy implements Subject { private RealSubject realSubject; public void Request() { if (realSubject==null) { realSubject=new RealSubject(); } preRequest(); realSubject.Request(); postRequest(); } public void preRequest() { System.out.println("访问真实主题之前的预处理。"); } public void postRequest() { System.out.println("访问真实主题之后的后续处理。"); } } public static void main(String[] args) { Proxy proxy=new Proxy(); proxy.Request(); }
运行结果
访问真实主题之前的预处理。 访问真实主题方法... 访问真实主题之后的后续处理。
🚓6.3、代理模式的应用实例
interface Manager { void doSomething(); } class Admin implements Manager{ @Override public void doSomething() { System.out.println("来自Admin方法doSomething!"); } } class AdminProxy implements Manager{ Admin admin; public AdminProxy(Admin admin){ this.admin = admin; } @Override public void doSomething() { System.out.println("----开始了----"); admin.doSomething(); System.out.println("----结束了----"); } } public static void main(String[] args) { AdminProxy ap = new AdminProxy(new Admin()); ap.doSomething(); }
运行结果
----开始了---- 来自Admin方法doSomething! ----结束了----
🚔7、动态代理
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(Proxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
🚕7.1、JDK动态代理
代理步骤:
定义一个事件管理器类实现InvocationHandler接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。
实现被代理类及其实现的接口。
调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。
通过该代理实例调用方法。
实现代码:
public interface Moveable { void move() throws Exception; } //2. 真实主题 public class Car implements Moveable { public void move() throws Exception { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽车行进中…"); } } //3.事务处理器 public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target) { super(); this.target = target; } /** * 参数: *proxy 被代理的对象 *method 被代理对象的方法 *args 方法的参数 *Object 方法返回值 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println("汽车开始行进…"); method.invoke(target, args); long stopTime = System.currentTimeMillis(); System.out.println("汽车结束行进…汽车行驶时间:" + (stopTime - startTime) + "毫秒!"); return null; } } public static void main(String[] args) throws Exception { Car car=new Car(); InvocationHandler h=new TimeHandler(car); Moveable moveable=(Moveable)Proxy.newProxyInstance(car.getClass().getClassLoader(),car.getClass().getInterfaces(),h); moveable.move(); }
运行结果
汽车开始行进… 汽车行进中… 汽车结束行进…汽车行驶时间:19毫秒!
在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。
🚖7.2、cglib动态代理
Java只允许单继承,而JDK生成的代理类本身就继承了Proxy类
,因此,使用JDK实现的动态代理不能完成继承式的动态代理,但是我们可以使用cglib来实现继承式的动态代理。
public void move(){ System.out.println("火车行驶中…"); } } public class CGLibProxy implements MethodInterceptor { private Object target; public Object getProxy(Object target) { this.target=target; Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //代理类调用父类的方法 System.out.println("日志开始"); methodProxy.invokeSuper(o,objects); System.out.println("日志结束"); return null; } } public static void main(String[] args) { CGLibProxy proxy=new CGLibProxy(); Train res = (Train) proxy.getProxy(new Train()); res.move(); }
运行结果:
日志开始 火车行驶中… 日志结束
分析:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 确实很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补,它允许类在不实现接口的前提下而实现动态代理。
🚗8、装饰模式
🚘8.1、装饰模式的定义与特点
装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰(Decorator)模式的主要优点有:
采用装饰模式扩展对象的功能比采用继承方式更加灵活。
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
🚙8.2、装饰模式的结构与实现
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。下面来分析其基本结构和实现方法。
1、模式的结构
装饰模式主要包含以下角色。
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
2、模式的实现
装饰模式的实现代码如下:
//抽象构件角色 interface Component { public void operation(); } //具体构件角色 class ConcreteComponent implements Component { public ConcreteComponent() { System.out.println("创建具体构件角色"); } public void operation() { System.out.println("调用具体构件角色的方法operation()"); } } //抽象装饰角色 class Decorator implements Component { private Component component; public Decorator(Component component) { this.component=component; } public void operation() { component.operation(); } } //具体装饰角色 class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); } public void operation() { super.operation(); addedFunction(); } public void addedFunction() { System.out.println("为具体构件角色增加额外的功能addedFunction()"); } } public static void main(String[] args) { Component p=new ConcreteComponent(); p.operation(); System.out.println("---------------------------------"); Component d=new ConcreteDecorator(p); d.operation(); }
运行结果
创建具体构件角色 调用具体构件角色的方法operation() --------------------------------- 调用具体构件角色的方法operation() 为具体构件角色增加额外的功能addedFunction()
🚚8.3装饰模式的应用实例
首先定义一个接口Human
public interface Human { public void run(); }
其次定义一个被装饰的类Man
public class Man implements Human { @Override public void run() { System.out.println("人会跑步"); } }
然后定义一个装饰的抽象类
public class ManDecorator extends AbstractDecorator { public ManDecorator(Human human) { //调用父类的构造方法 super(human); } //装饰类增加的功能 private void fly() { System.out.println("人也许飞哦"); } //增强了功能的run方法 @Override public void run() { super.run(); fly(); } }
最后定义一个装饰的实现类
public class ManDecorator extends AbstractDecorator { public ManDecorator(Human human) { //调用父类的构造方法 super(human); } //装饰类增加的功能 private void fly() { System.out.println("人也许飞哦"); } //增强了功能的run方法 @Override public void run() { super.run(); fly(); } }
主方法:
public static void main(String[] args) { //创建被装饰的类 Human human = new Man(); //创建装饰的类,并添加被装饰类的引用 Human superMan = new ManDecorator(human); //执行增强后的run方法 superMan.run(); }
运行结果
人会跑步 人也许飞哦
🚛8.3、装饰模式的应用场景
当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
装饰模式在 Java语言中的最著名的应用莫过于 Java I/O 标准库的设计了。
例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。
例如:为 FileReader 增加缓冲区而采用的装饰类 BufferedReader
BufferedReader in= new BufferedReader(new FileReader("filename.txt")); String s=in.readLine();
🚜9、适配器模式
🏎9.1、适配器模式的定义与特点
适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种。
适配器模式的主要优点如下:
客户端通过适配器可以透明地调用目标接口。
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
🏍9.2、适配器模式的结构与实现
1、模式的结构
适配器模式(Adapter)包含以下主要角色。
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式的结构图如图所示:
对象适配器模式的结构图如图所示
2、模式的实现
(1) 、类适配器模式的代码
//目标接口 interface Target { public void request(); } //适配者接口 class Adaptee { public void specificRequest() { System.out.println("适配者中的业务代码被调用!"); } } //类适配器类 class ClassAdapter extends Adaptee implements Target { public void request() { specificRequest(); } } //客户端代码 public class ClassAdapterTest { public static void main(String[] args) { System.out.println("类适配器模式测试:"); Target target = new ClassAdapter(); target.request(); } }
运行结果
类适配器模式测试: 适配者中的业务代码被调用!
(2)、对象适配器模式的代码
//目标接口 interface Target { public void request(); } //适配者接口 class Adaptee { public void specificRequest() { System.out.println("适配者中的业务代码被调用!"); } } //对象适配器类 class ObjectAdapter implements Target { private Adaptee adaptee; public ObjectAdapter(Adaptee adaptee) { this.adaptee=adaptee; } public void request() { adaptee.specificRequest(); } } //客户端代码 public static void main(String[] args) { System.out.println("对象适配器模式测试:"); Adaptee adaptee = new Adaptee(); Target target = new ObjectAdapter(adaptee); target.request(); }
模式的应用实例
public interface Restaurant { public void haveFood(); public void haveDrink(); } public class RestaurantImpl implements Restaurant{ @Override public void haveFood() { System.out.println("提供各种美食"); } @Override public void haveDrink() { System.out.println("提供各种饮料"); } } public interface Bar { public void haveSong(); } public class MyRestaurantAdapter extends RestaurantImpl implements Bar{ @Override public void haveSong() { System.out.println("在餐厅同样提供酒吧的驻唱服务"); } } public static void main(String[] args) { MyRestaurantAdapter adapter = new MyRestaurantAdapter(); adapter.haveFood(); adapter.haveDrink(); adapter.haveSong(); }
运行结果
提供各种美食 提供各种饮料 在餐厅同样提供酒吧的驻唱服务
🚧9.3、适配器模式的应用场景
适配器模式(Adapter)通常适用于以下场景。
以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
🛑10、观察者模式
🚦10.1、观察者模式的定义与特点
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优点如下:
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
目标与观察者之间建立了一套触发机制。
🚥10.2、观察者模式的结构与实现
1、模式的结构
观察者模式的主要角色如下:
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
2、模式的实现
观察者模式的实现代码
//抽象目标 abstract class Subject { protected List<Observer> observers=new ArrayList<Observer>(); //增加观察者方法 public void add(Observer observer) { observers.add(observer); } //删除观察者方法 public void remove(Observer observer) { observers.remove(observer); } public abstract void notifyObserver(); //通知观察者方法 } //具体目标 class ConcreteSubject extends Subject { public void notifyObserver() { System.out.println("具体目标发生改变..."); System.out.println("--------------"); for(Object obs:observers) { ((Observer)obs).response(); } } } //抽象观察者 interface Observer { void response(); //反应 } //具体观察者1 class ConcreteObserver1 implements Observer { public void response() { System.out.println("具体观察者1作出反应!"); } } //具体观察者1 class ConcreteObserver2 implements Observer { public void response() { System.out.println("具体观察者2作出反应!"); } } public static void main(String[] args) { Subject subject=new ConcreteSubject(); Observer obs1=new ConcreteObserver1(); Observer obs2=new ConcreteObserver2(); subject.add(obs1); subject.add(obs2); subject.notifyObserver(); }
运行结果
具体目标发生改变... -------------- 具体观察者1作出反应! 具体观察者2作出反应!
🚨10.3、观察者模式的应用实例
利用观察者模式设计一个程序,分析“人民币汇率”的升值或贬值对进口公司的进口产品成本或出口公司的出口产品收入以及公司的利润率的影响。
//抽象目标:汇率 abstract class Rate { protected List<Company> companys=new ArrayList<Company>(); //增加观察者方法 public void add(Company company) { companys.add(company); } //删除观察者方法 public void remove(Company company) { companys.remove(company); } public abstract void change(int number); } //具体目标:人民币汇率 class RMBrate extends Rate { public void change(int number) { for(Company obs:companys) { ((Company)obs).response(number); } } } //抽象观察者:公司 interface Company { void response(int number); } //具体观察者1:进口公司 class ImportCompany implements Company { public void response(int number) { if(number>0) { System.out.println("人民币汇率升值"+number+"个基点,降低了进口产品成本,提升了进口公司利润率。"); } else if(number<0) { System.out.println("人民币汇率贬值"+(-number)+"个基点,提升了进口产品成本,降低了进口公司利润率。"); } } } //具体观察者2:出口公司 class ExportCompany implements Company { public void response(int number) { if(number>0) { System.out.println("人民币汇率升值"+number+"个基点,降低了出口产品收入,降低了出口公司的销售利润率。"); } else if(number<0) { System.out.println("人民币汇率贬值"+(-number)+"个基点,提升了出口产品收入,提升了出口公司的销售利润率。"); } } } public static void main(String[] args) { Rate rate=new RMBrate(); Company watcher1=new ImportCompany(); Company watcher2=new ExportCompany(); rate.add(watcher1); rate.add(watcher2); rate.change(10); rate.change(-9); }
运行结果
人民币汇率升值10个基点,降低了进口产品成本,提升了进口公司利润率。 人民币汇率升值10个基点,降低了出口产品收入,降低了出口公司的销售利润率。 人民币汇率贬值9个基点,提升了进口产品成本,降低了进口公司利润率。 人民币汇率贬值9个基点,提升了出口产品收入,提升了出口公司的销售利润率。
⛽️10.4、观察者模式的应用场景
通过前面的分析与应用实例可知观察者模式适合以下几种情形。
对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。