常见的设计模式
- spring下默认的bean均为singleton,可以通过singleton=“true|false” 或者 scope=“?”来指定
- spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。
- java.util.logging.Logger#log() 责任链模式,避免请求的发送者和接收者之间的耦合关系
- java.util.Pattern 解释器模式
- java.util.Iterator、java.util.Scanner 迭代器模式
- java.lang.reflect.Method#invoke() java.util.concurrent.Executor#execute() 中介者模式,集中相关对象之间复杂的沟通和控制方式
- spring ApplicationListener 观察者模式
- spring中在实例化对象的时候用到策略模式
- spring JdbcTemplate中的execute方法、xxxApplicationContext 的refush()方法使用模板方法
- Arrays.asList() 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
- sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是为对象动态添加功能。
- Java 利用缓存来加速大量小对象的访问时间。java.lang.Integer#valueOf(int)
- spring的代理模式在aop中有体现(控制:对功能的限制)
静态代理: 程序运行前就已存在的编译好的代理类。实现步骤:
1、定义业务接口。
2、实现业务接口。
3、定义代理类并实现业务接口,最后通过客户端调用。
动态代理:程序运行期间根据需要动态创建代理类及其实例已完成功能
public class CodingServiceProxy implements CodingService{
private CodingService codingService;
public CodingServiceProxy(CodingService codingService){
this.codingService = codingService;
}
@Override
public String debug(String name) {
System.out.println("预处理...");
String result = codingService.debug(name);
System.out.println(result);
System.out.println("后处理...");
return result;
}
代码重构 - 尽量做到每个方法都可以单元测试
- 大量if/else
- 利用反射处理(效率很低)
private static Map methodsMap = new HashMap<>();
//首先取出状态码,然后根据状态码获得相应的要调用方法的方法名,然后使用java的反射机制就可以实现对应方法的调用了。
static {
// value 是属性CountRecoder的方法名
methodsMap.put(1, "setCountOfFirstStage");
methodsMap.put(2, "setCountOfSecondStage");
methodsMap.put(3, "setCountOfThirdtage");
}
public CountRecoder getCountRecoderByReflect(List countEntries) {
CountRecoder countRecoder = new CountRecoder();
countEntries.stream().forEach(countEntry -> fillCount(countRecoder, countEntry));
return countRecoder;
}
private void fillCount(CountRecoder shippingOrderCountDto, CountEntry countEntry) {
String name = methodsMap.get(countEntry.getCode());
try { // .class.getMethod(方法名,方法返回值) -》 invoke 插入到这个实体类
Method declaredMethod = CountRecoder.class.getMethod(name, Integer.class);
declaredMethod.invoke(shippingOrderCountDto, countEntry.getCount());
} catch (Exception e) {
System.out.println(e);
}
}
- 策略模式处理
定义一个抽象接口,里面定义要实现的方法。每个子类封装不同的算法去处理它,再通过map去调用。
public class FillCountServieFactory {
private static Map fillCountServiceMap = new HashMap<>();
static {
fillCountServiceMap.put(1, new FirstStageService());
fillCountServiceMap.put(2, new SecondStageService());
}
public static FillCountService getFillCountStrategy(int statusCode) {
return fillCountServiceMap.get(statusCode);
}
}
- 消除重复代码 - 模板方法
- 我的实战 采用工厂模式+策略模式 重构
- 解析上传文件格式是否正确 判断是周报还是月报
- 解析内容数据入库
- 上传成功后,保存上传时间至Redis
- 保存相应文件信息
将各种月报解析算法封装到策略模式的service中(步骤是一致的,所以都继承一个抽象类)。
进入方法是,通过一个键为月报类型(枚举得到),值为月报解析策略的map确定解析步骤。
策略模式:负责对算法封装,保证两个策略自由切换,以后增加策略也很容易
工厂模式:修正策略模式必须对外暴露问题,由工厂模式产生一个具体策略对象
对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
Fruit fruit = null;
try {
fruit = (Fruit) Class.forName(fruitKindPath).newInstance();
} catch (Exception e){
System.out.println(e.getMessage());
}
设计原则
开闭原则(总则)
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
单一职责原则 - 职责单一
建议是接口一定要做到单一职责,类的设计尽量做到只有一个 原因引起变化。
里氏替换原则 - 不要破坏继承体系
只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
依赖倒置原则 - 面向接口编程
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
接口隔离原则 - 在设计接口的时候要精简单一
建立单一接口,不要建立臃肿庞大的接口。(通过分散定义多个接口,可以预防未来变更的扩散,提高系统的灵活性和可维护性。)
迪米特法则 - 降低耦合
一个类应该对自己需要耦合
或调用的类知道得最少。
适应设计模式
Iterator迭代器模式 - 一个一个遍历
引入Iterator后可以将遍历与实现分离开。
Visitor 模式在遍历元素集合的过程中,对元素进行相同的处理。
Adapter适配器模式 - 加适配便于复用
- 类适配器模式(继承)
- 对象适配器模式(委托)
Adapter模式用于连接接口(API)不同的类,Bridge用于链接类的功能层次结构与实现层次结构。
Adapter模式用于填补不同接口(API)之间的缝隙,Decorator模式则在不改变接口(API)的前提下增加功能。
匿名内部类+适配器结合使用
交给子类
模板方法Template Method - 将具体处理交给子类
在父类种定义处理流程的框架,在子类中实现具体处理。
抽象类不仅负责实现模板方法,还负责声明在模板方法种所使用到的抽象方法。
Factory Method将模板方法用于生成实例。
Template Method通过继承改变程序行为,用于改变部分行为;Strategy使用委托改变程序行为,用于替换整个算法。
Factory Method工厂模式 - 将实例的生成交给子类
Factory 方法实现方式一般有以下3种。
- 指定其为抽象方法
- 为其实现默认处理
- 在其中抛出异常
生成实例
Singleton单例模式 - 只有一个实例
- 懒汉模式-双重校验锁-线程安全
- 懒汉模式-静态内部类实现 ( JVM 提供了对线程安全的支持)
public class Singleton {
private Singleton() {
}
// 只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
- 饿汉模式-枚举实现
防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次。
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
}
Prototype原型模式 - 通过复制生成
- 对象种类繁多,无法整合进一个类
- 难以根据类生成实例
- 解耦框架与生成的实例
- Prototype(原型)定义用于复制现有实例来生成新实例的方法。
- Client(使用者)使用复制实例的方法生成新实例。
- ConcretePrototype(具体的原型)复制现有实例生成新实例。
Prototype生成与当前实例状态相同的实例。Flyweight可以在不同地方使用同一实例
使用Memento可以保存当前实例的状态,以实现快照和撤销功能
实现Cloneable
接口(标记接口)就能调用clone()
方法进行实体的浅复制。
Builder建造者模式 - 组装复杂的实例
只有不知道子类才能替换,设计时能够决定一部分事情
- Builder建造者,定义用于生成实例的接口(API)
- Director监工,使用Builder接口生成实例
- ConcreteBuilder具体建造者,实现Builder接口的类
Abstract Factory 抽象工厂 - 将关联组件组装成产品
Java中生成实例方法:
1.new
2.clone
3.obj.getClass().newInstance()
分开考虑
Bridge桥梁模式-将类的功能层次结构与实现层次结构分离
它将 功能层次结构 / 实现层次结构 链接起来。
当想要增加功能时,只需在 功能层次结构 增加类即可。
使用委托的方式,交给其它类实现。 (弱化类之间的关联关系)
Strategy策略模式 - 整体替换算法
定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。
不管我们使用哪一种出行方式,最终的目的地都是一样的。也就是选择不同的方式产生的结果都是一样的。
工厂模式是你决定哪种旅行方案后,由工厂代替你去构建具体方案(工厂代替你去买火车票)。
一致性
Composite混合模式 - 容器与内容的一致性
能够使容器与内容的具有一致性,创造出递归结构。(树结构适用)
容器 -》 里面能放书或者自身(目录)
Decorator装饰模式 - 装饰边框与被装饰物的一致性
访问数据结构
Visitor模式 - 访问数据结构并处理数据
将处理从数据结构中分离开来。提高作为组件的独立性。
Chain of Responsibility责任链模式 - 推卸责任
将多个对象组成一条职责链,然后按照它们在职责链上的顺序一个一个找出到底谁负责处理。
简单化
Facade简单窗口模式
当出现(在调用这个类之前先调用这个类注册一下)这种操作时,就需要窗口模式了。
它让复杂的东西变得看起来简单。
Mediator中介者模式 - 只有一个仲裁者
要调整多个对象之间的关系。不让他们各自通信,而是让他们与仲裁者通信。
管理状态
Observer观察者模式 - 发送状态变化通知
Memento备忘录模式 - 保存对象的状态
事先将某个时间点的实例状态保存下来,在必要时将实例恢复至当时的状态。
State状态模式 - 用类表示状态
- 用方法来判断状态
- 用类来表示状态(为每一种具体的状态都定义相应的类)实现方式:1.定义接口,声明抽象方法。2.定义多个类,实现具体方法。
避免浪费
Flyweight享元模式 - 共享对象,避免浪费
通过共享实例来避免new出实例。(要注意不要被垃圾回收器回收了)
Proxy代理模式 - 只在必要时生成实例
为其他对象提供一种代理以控制对这个对象的访问。
- Proxy角色实现了主体定义的接口API(透明性),同时尽量处理来自Client的请求。只在必要时才去生成RealSubject(本人)
用类来表现
Command命令模式 - 命令也是类
Interpreter解释器模式 - 语法规则也是类
用迷你语言推导出来语法树。
应用:正则表达式 / 检索表达式 / 批处理语言