简单学习设计模式(观察者模式、策略模式)
本篇文章是我在公开课上学习的内容,为了方便加深理解,在这里做个笔记,这里只用了两个简单的需求作为例子简单的介绍了观察者模式及策略模式。
六大设计原则
1、单一职责原则:一个类只负责一个功能领域中的相应职责。高内聚、低耦合。
2、开闭原则:对修改关闭。不修改原有代码的情况下进行扩展
3、里氏代换 原则:所有引用基类(父类)的地方必须能透明地使用其子类的对象。
4、依赖倒转原则:抽象不应该依赖于细节,细节应当依赖于抽象。
5、接口隔离原则:接口拆分。当一个接口太大时,我们需要将它分割成一些更小的接口。
6、迪米特法则:减少依赖。一个软件实体应当尽可能少的减少与其他实体发生相互作用。
简单需求例子-观察者模式
需求:用户下单的时候发送短信给用户,以后也可能下单时候做其他不确定的需求操作。
编码设计(符合单一职责原则,符合开闭原则)。
spring框架内部拓展
spring启动后,会执行内部一系列的操作。我们经常定义spring在启动之后,执行一些特定业务代码。我们可以通过这样的方式拓展我们的业务功能。
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时候,所有依赖它的对象都被通知并自动更新。(又称:发布/订阅、消息通知机制、事件监听、事件驱动编程)
一般情况下,若想让自己的业务代码解耦,都可以用观察者模式。
利用观察者模式设计高拓展性的代码,利用spring事件机制改造订单功能。
当订单事件发生后,订单业务逻辑结束,同时利用spring的applicationEvent发布事件。
以下简单写一段订单事件的业务伪代码,主要三部分一是订单操作类OrderService,一个是订单事件类OrderEvent 需继承ApplicationEvent,一个是具体拓展操作类SmsListener、Wxlistener,拓展类需要实现spring的接口ApplicationListener。
OrderService:
@Service
public class OrderService {
@Autowired
ApplicationContext applicationContext ;
//新的需求需要更多的操作时
//需满足单一职责原则、开闭原则
public void saveOrder() {
//订单入库
System.out.println("1、下单成功!");
//发布事件
Thread thread = new Thread(new publishThread( new OrderEvent(""),applicationContext));
thread.start();
System.out.println("流程结束");
//平常写法如下(违反单一职责原则,违反开闭原则)
//System.out.println("2、短信发送成功!");//发送短息操作
//System.out.println("3、微信发送成功"); //发送微信操作
}
/**
* 发布订单相关事件线程
* @author
* @date 2020年12月11日
* @time 下午11:54:59
*/
public class publishThread extends Thread{
OrderEvent orderEvent;
ApplicationContext applicationContext;
public publishThread(OrderEvent orderEvent ,ApplicationContext applicationContext) {
this.orderEvent = orderEvent;
this.applicationContext= applicationContext;
}
@Override
public void run() {
//发布事件
applicationContext.publishEvent(orderEvent);
}
}
}
OrderEvent :
public class OrderEvent extends ApplicationEvent{
public OrderEvent(Object source) {
super(source);
//也可以重写覆盖
}
}
SmsListener:
@Component
public class SmsListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent orderEvent) {
System.out.println("2、发送短信成功!");
}
}
Wxlistener:
@Component
public class Wxlistener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent arg0) {
System.out.println("3、发送微信成功!");
}
}
用观察者模式一般情况下都能够写出即规范同时也符合业务需求的代码,在业务需要拓展的时候,只需要添加新的类去实现方法即可。
简单需求例子-策略模式
需求:用户下单的实际价格根据不同用户类型得到不同的价格,用户类型可能不断增多。
编码设计(符合单一职责原则,符合开闭原则)。
策略模式
思路:首先写好公用价格的算法接口,针对不同用户设计自己的具体算法类并实现公用的算法接口,根据不同的用户类型调用不同算法类返回价格。
代码如下:
业务代码 SaleService :
/**
* Spring作为一个容器,管理着一个项目中所有经过配置的Java类 (Annotation方式或xml配置文件)。
* 如果某个接口的所有实现类均被Spring托管了,那么通过Spring就可以很简 单的返回这些实现类。
*
* @date 2020年12月13日
* @time 下午10:08:38
*/
public class SaleService implements ApplicationContextAware{
@Autowired
NormalPrice normalPrice;
@Autowired
VipPrice vipPrice;
/**
*
* @author
* @date 2020年12月13日
* @time 下午8:00:01
* @param userTyep
* @param price
* @return
*/
public double sale(String userType,double price) {
//1、常规写法,符合单一职责原则,不符合开闭原则
if(userType.equals("normal")) {
price=price*1.0;
}else if(userType.equals("vip")){
price= price*0.9;
}else if(userType.equals("svip")) {
price = price * 0.8;
}
//2、策略模式抽离方法,代码简洁,但不符合开闭原则
if(userType.equals("normal")) {
price=normalPrice.disCount(price);
}else if(userType.equals("vip")){
price= vipPrice.disCount(price);
}else if(userType.equals("svip")) {
price = price * 0.8;
}
return price;
}
//3、满足单一职责原则,满足开闭原则
Map<String, CalculatePrice> map = new HashMap<>();
Map<String, CalculatePrice> map2= new HashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取所有实现 CalculatePrice 接口的类名及类
map = applicationContext.getBeansOfType(CalculatePrice.class);
for (Map.Entry<String, CalculatePrice> entry: map.entrySet()) {
//封装 算法的 用户类型及算法类
map2.put(entry.getValue().userType(), entry.getValue());
}
}
//计算价格
public double sale2(String userTyep,double price) {
return map2.get(userTyep).disCount(price);
}
}
公用算法接口 CalculatePrice:
public interface CalculatePrice {
String userType();
double disCount(double price);
}
普通用户价格算法类:NormalPrice
@Service
public class NormalPrice implements CalculatePrice{
@Override
public String userType() {
// TODO Auto-generated method stub
return "normal";
}
@Override
public double disCount(double price) {
// TODO Auto-generated method stub
return price*1.0;
}
}
vip用户价格算法类 VipPrice :
@Service
public class VipPrice implements CalculatePrice{
@Override
public String userType() {
return "vip";
}
@Override
public double disCount(double price) {
return price*0.9;
}
}
以上在业务层中,示例了三种方式对比,其中只有第三种既符合单一职责原则同时符合开闭原则。第三种方式通过spring容器获取到了所有实现 CalculatePrice接口的类,再通过自己的封装,从而达到了新功能拓展且不用再更改业务层代码的目的。