场景: 商场搞活动,根据客户购买商品的金额,收费时给与不同的打折,比如,购买 金额>=2000 的打八折(0.8),金额 500 ~ 1000 的,打九折(0.9),购买金额 0 ~ 500 的九五折(0.95),根据不同的金额走不同计算策略逻辑。
3.1 策略模式
首先定义一个Strategy接口来表示一个策略:
其中strategy方法返回当前策略的唯一标识,algorithm则是该策略的具体执行的计算逻辑。
下面是Strategy接口的两个实现类:
自定义策略选择枚举 StrategySelector:
然后定义一个StrategyRunner接口用来表示策略的调度器:
execute方法内部通过判断strategy的值来决定具体执行哪一个策略。
在StrategyRunnerImpl内部,定义了一个STRATEGIES
列表来保存所有Strategy实现类的实例,以及一个叫做STRATEGY_MAP
的Map来保存strategy和Strategy实例之间的对应关系,static块中的代码用于从STRATEGIES
列表构造STRATEGY_MAP
。
这样,在execute方法中就可以很方便地获取到指定strategy的Strategy实例。
实现并运用策略模式
然后,定义一个StrategyConfig配置类,用于向容器注入一个StrategyRunner:
不难发现,strategyRunner方法的实现,其中的逻辑与之前的StrategyRunnerImpl几乎完全相同,也是根据一个List<Strategy>
来构造一个Map<String, Strategy>
。
只不过,这里的strategies列表不是我们自己构造的,而是通过方法参数传进来的。
由于strategyRunner标注了Bean注解,因此参数上的List<Strategy>
实际上是在Spring Boot初始化过程中从容器获取的,所以我们之前向容器中注册的那两个实现类会在这里被注入。
这样,我们再也无需操心系统中一共有多少个Strategy实现类,因为Spring Boot的自动配置会帮我们自动发现所有实现类。我们只需编写自己的Strategy实现类,然后将它注册进容器,并在任何需要的地方注入StrategyRunner:
然后直接使用strategyRunner就行了:
访问接口,控制台输出如下:
3.2 简单工厂模式
举个场景例子🌰:
用户支付场景,目前支持支付宝支付和微信支付,未来会新增银行卡,云闪付等方式。使用策略模式,每一种支付方式都是一种策略,根据用户传入的支付类型,创建不同的策略类,使用工厂模式,通过封装一个PaymentStrategyHandler策略处理类,其他系统直接通过一个统一的入口,进行该功能的调用,使用门面模式。
3.2.1 定义一个策略类:
3.2.2 创建策略工厂
3.3.3 定义策略枚举
3.3.4 创建策略的上下文角色
3.4.5 提供统一访问处理入口
3.4.6 创建Controller
3.3 单例模式
代码演示:
3.4 代理模式
代理模式Proxy, 为其他对象提供一种代理以控制对这个对象的访问。
代理模式 实际上在平时中也运用的非常广泛,最经典的例子就是房东委托中介代理出租房子的案例,本文也是采用这个案例对代理模式进行解释和代码实现。
代码实例🌰:
创建一个Subject类:
定义一个房东角色,现在活动类:
定义一个中介代理对象:
模拟用户找中介租房子:
3.5 工厂方法模式
上面我们也提到了简单工厂模式,那么工厂方法模式和简单工厂的区别在于哪里呢,其实,简单工厂模式的最大优点在于包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,相对于客户端来说,去除了与具体产品的依赖。
工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类,通俗来说:它提供了一种实例化逻辑委托子类的方法。
代码示例:
定义NetworkConfigFactoryService
工厂类
NetworkConfigFactoryService
工厂实现类
定义网点操作接口NetworkConfigCrudService
它的实现类分别是 AServiceImpl
、BServiceImpl
、CServiceImpl
、DServiceImpl
,分别对应不同的逻辑:
控制层NetworkConfigController
3.6 观察者模式
观察者模式Observer 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
初识观察者模式:报社+订阅者 = 观察者模式。
要点
- • 观察者模式定义了对象之间一对多的关系。
- • 主题(可观察者)用一个共同的接口来更新观察者。
- • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
- • 使用次模式时,你可以从被观察者处推或拉数据(推的方式被认为是更正确的)。
- • 有多个观察者时,不可以依赖特定的通知次序。
- • java中有多种观察者模式的实现,包括了通用的
java.util.Observable
,不过需要注意Observable实现上所带来的问题,有必要的话,可以实现自己的Observable。 - • Spring也大量使用观察者模,比如ListenrEvent消息订阅与发布;
案例
直接以气象站为例,其中天气信息就表示被观察者,天气布告板就表示订阅者和观察者,当天气发生变化(被观察者)时,会通过notifyObserver通知所有观察者,并调用他们的控制方法处理数据。
一个WeatherData对象负责追踪目前的天气状况(温度,湿度,气压)。我们希望你们能建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。
气象监测应用的对象分析
此系统中的三个部分是:
- • 气象站(获取实际气象数据的物理装置)
- • WeatherData对象(最总来自气象站的数据,并更新布告板)
- • 布告板(显示目前天气状况给用户看)。
实现气象站
在WeatherData中实现主题接口
建立布告板
其中的一个布告板:
利用内置的支持重写WeatherData
利用内置观察者重写布告板
3.7 模板方法模式
模板方法(Template Method) 是一种行为设计模式。模板方法设计模式用于创建方法存根并将某些实现步骤推迟到子类。
模板方法定义了执行算法的步骤,它可以提供可能对所有或者部分子类通用的默认实现,下面通过一个简单的例子来理解这个模式,假设我们想提供一种算法了该房子,建造房屋需要执行的步骤是:建造地基->建造支柱->建造墙壁和窗户。
重点的一点是我们不能改变执行的顺序,因为我们不能在构建基础之前构建窗口,所以在这种情况下,我们可以创建一个模板方法,它将使用不同的方法来建造房子,现在盖房子的地基对于所有类型的房子都是一样的,无论是木房、玻璃房子还是混泥土房。
所以我们可以为此提供基础实现,如果子类想要覆盖这个方法,他们可以自己选择,但是大多数情况下,所有类型的房屋都很常见。为了确保子类不覆盖模板方法,我们应该将其设为最终方法。
模板方法抽象类
由于我们希望某些方法由子类实现,因此我们必须将我们的基类设为抽象类。
定义抽象类HouseTemplate
WoodenHouse
GlassHouse
ConcreteHouse
HousingClient
输出结果:
3.8 适配器模式
适配器模式Adapter 是将一个接口转换成另一个客户所期望的接口。Adapter 适配器让那些本来因为接口不兼容的类可以合作无间。
适配器模式中的角色分析
- • 目标接口(Traget):客户期待的接口,目标可以是具体的或者抽象的类,也可以是接口。
- • 需要适配的对象(Source Adaptee):需要适配的对象。
- • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转成目标接口。
举个例子🌰:我们以网线上网为例,现在有一根水晶头网线,但是它的接口与电脑的不匹配(因为电脑的是usb或者typec),那么就需要一个转接头,也就是我们说的适配器,才能够上网,下面的转接头可以理解为适配器:
类适配器
首先我们拥有一根网线, 他有上网的功能,但是它的接口与电脑不匹配:
因此我们定义了一个usb接口,也就是上面提到的目标接口(Target):
定义一个适配器继承着网线,连接着usb接口:
上网的具体实现:
对象适配器应用
- •
java.util.Arrays#asList()open in new window
- •
java.util.Collections#list()open in new window
- •
java.util.Collections#enumeration()open in new window
- •
javax.xml.bind.annotation.adapters.XMLAdapter
四、总结
设计模式(Design pattern) 代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
最后说一句(求关注!别白嫖!)
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!