观察者模式
1.1、观察者模式概述
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系(注册),使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新(通知)。说白了就是个注册,通知的过程。
观察者模式的主要角色
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
1.2、观察者模式的优缺点
观察者模式是一种对象行为型模式,其主要优点如下:
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
目标与观察者之间建立了一套触发机制。
它的主要缺点如下:
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
1.3、观察者模式注意事项
1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
1.4、异步处理线程安全问题
被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看 Message Queue,就会有更深的了解。
class Producer {
private List<IConsumer> consumerList = new ArrayList<>();
public void add(IConsumer consumer) {
consumerList.add(consumer);
}
public void notifyObserver(String message){
for (IConsumer obs:consumerList){
obs.response(message);
}
}
}
class CurrentProducer extends Producer {
public void send(String message) {
System.out.println("发送消息:"+message);
super.notifyObserver(message);
}
}
interface IConsumer {
void response(String message);
}
class Consumer implements IConsumer {
@Override
public void response(String message) {
System.out.println("消费者1接收消息:"+message);
}
}
class Consumer2 implements IConsumer {
@Override
public void response(String message) {
System.out.println("消费者2接收消息:"+message);
}
}
public class Test1 {
public static void main(String[] args) {
CurrentProducer producer = new CurrentProducer();
IConsumer consumer = new Consumer();
IConsumer consumer2 = new Consumer2();
producer.add(consumer);
producer.add(consumer2);
producer.send(UUID.randomUUID().toString());
}
}
输出结果
发送消息:89f997f6-4688-4950-9d3f-5d86615098fd
消费者1接收消息:89f997f6-4688-4950-9d3f-5d86615098fd
消费者2接收消息:89f997f6-4688-4950-9d3f-5d86615098fd
策略模式
1.策略模式简介
策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化
2.模式结构
策略模式包含如下角色:
Strategy: 抽象策略类:策略是一个接口
Context: 环境类 /上下文类
ConcreteStrategy: 具体策略类
案例
电商购物车系统价格计算:
1.普通会员没有优惠
2.中级会员打9折
3.高级会员打8折
1).传统实现方式
public Double calculationPrice(String type, Double originalPrice, int n) {
//中级会员计费
if (type.equals("intermediateMember")) {
return originalPrice * n * 0.9;
}
//高级会员计费
if (type.equals("advancePrimaryMember")) {
return originalPrice * n * 0.8;
}
//普通会员计费
return originalPrice;
}
2).策略模式实现
抽象类策略
interface MemberStrategy {
// 一个计算价格的抽象方法
//price商品的价格 n商品的个数
double calcPrice(double price, int n);
}
具体实现类
// 普通会员——不打折
class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double price, int n) {
return price * n;
}
}
// 中级会员 9折
class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double price, int n) {
return price * n * 0.9;
}
}
// 高级会员 8折
class AdvanceMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double price, int n) {
return price * n * 0.8;
}
}
上下文类或环境类,起承上启下封装作用
class MemberContext {
private MemberStrategy memberStrategy;
public MemberContext(MemberStrategy memberStrategy) {
this.memberStrategy = memberStrategy;
}
//计算价格
public double qoutePrice(double goodsPrice, int n) {
// 通过接口变量调用对应的具体策略
return memberStrategy.calcPrice(goodsPrice, n);
}
}
测试类
public class test1 {
public static void main(String[] args) {
//普通会员价格计算
PrimaryMemberStrategy primaryMemberStrategy = new PrimaryMemberStrategy();
MemberContext memberContext = new MemberContext(primaryMemberStrategy);
//中级会员价格计算
IntermediateMemberStrategy intermediateMemberStrategy = new IntermediateMemberStrategy();
MemberContext memberContext2 = new MemberContext(intermediateMemberStrategy);
//高级会员价格计算
AdvanceMemberStrategy advanceMemberStrategy = new AdvanceMemberStrategy();
MemberContext memberContext3 = new MemberContext(advanceMemberStrategy);
//计算一本100块钱的书
System.out.println("普通会员的价格:"+ memberContext.qoutePrice(100,1));// 普通会员:100
System.out.println("中级会员的价格:"+ memberContext2.qoutePrice(100,1));// 中级会员 90
System.out.println("高级会员的价格:"+ memberContext3.qoutePrice(100,1));// 高级会员80
}
}
输出结果
普通会员的价格:100.0
中级会员的价格:90.0
高级会员的价格:80.0
4.策略模式优缺点
1)优点
策略模式提供了对“开闭原则”的完美支持,用户可以在不 修改原有系统的基础上选择算法或行为,也可以灵活地增加 新的算法或行为。
策略模式提供了管理相关的算法族的办法。
策略模式提供了可以替换继承关系的办法。
使用策略模式可以避免使用多重条件转移语句。
2)缺点
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
策略模式将造成产生很多策略类,可以通过使用享元模式在一 定程度上减少对象的数量。
5.策略模式适用场景
在以下情况下可以使用策略模式:
如果在一个系统里面有许多类,它们之间的区别仅在于它们 的行为,那么使用策略模式可以动态地让一个对象在许多行 为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
如果一个对象有很多的行为,如果不用恰当的模式,这些行 为就只好使用多重的条件选择语句来实现。
不希望客户端知道复杂的、与算法相关的数据结构,在具体 策略类中封装算法和相关的数据结构,提高算法的保密性与 安全性。
在我们生活中比较常见的应用模式有:
1、电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式
2、电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式
适配器模式
1.什么是适配器模式?
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作
2.适配器模式中的角色分析
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
3.实现方式
1).类适配器(采用继承实现)
2).对象适配器(采用对象组合方式实现)
4.类适配器:
因为 Adapter 类既继承了 Adaptee (被适配类),也实现了 Target 接口(因为 Java 不支持多继承,所以这样来实现)所以被称为类适配器
实例(以插网线上网举例):
1)首先我们拥有一根网线,他有上网的功能,但是它的接口与电脑不匹配
//要适配的类:网线
public class Adaptee {
//功能:上网
public void request(){
System.out.println("链接网线上网");
}
}
2)因此我们定义了一个usb接口,也就是上面提到的目标接口(Target)
//接口转换器的抽象实现
public interface NetToUsb {
//作用:处理请求,网线=>usb
public void handleRequest();
}
3)定义一个适配器继承着网线,连接着usb接口
public class Adapter extends Adaptee implements NetToUsb {
@Override
public void handleRequest() {
super.request();//可以上网了
}
}
对象适配器的优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标
可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据’里氏替换原则’适配者的子类也可以通过该适配器进行适配
类适配器的缺点 :
对于java,c#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不同同时适配多个适配者
在java,c#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
使用场景:
系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码