设计模式(一)设计原则
设计模式(一)设计原则
开闭原则
定义:一个软件实体,如类、模块、函数应该对扩展开放,对修改关闭。例如面向抽象编程,而不要面向实现编程。
优点:提高软件系统的可复用性、可维护性。
依赖倒置原则
定义:高层不应该依赖底层模块,二者都应该依赖抽象。要针对接口编程,不要针对实现编程。
优点:减少类的耦合性,提高稳定性、代码可读性、可维护性。
依赖倒置原则和开闭原则举例:
MyBatis在使用Mapper的时候
SqlSession sqlSession = sqlSessionFactory.openSession();
会调用DefaultSqlSessionFactory的openSessionFromDataSource方法。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
略
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
略
}
其中Configuration.newExecutor方法。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Configuration持有的引用是Executor接口,而Executor接口有3个实现类:
BaseExecutor抽象类使用模板方法模式,定义了通用的方法实现。供ReuseExecutor、SimpleExecutor、BatchExecutor调用。
SimpleExecutor是最简单的Executor接口实现,ReuseExecutor和SimpleExecutor的区别是:ReuseExecutor能重用Statement。而BatchExecutor能够批量执行sql。
CachingExecutor使用了装饰模式,给Executor接口实现类封装了二级缓存的功能,稍后专写一篇关于它的介绍。
这里就遵循了依赖导致原则和开闭原则,Configuration持有的引用是Executor接口,依赖的是Executor接口,而非具体的实现类。同时对扩展开放,如果要增加新的Executor,只需要新增加一个XXXExecutor,并在初始化的时候new即可。
单一职责原则
定义:一个类、接口、方法应该只有一个职责。
现实编码中,很难严格遵守,基本上没有哪个类做到单一职责。建议方法级别做到单一职责原则。
优点:降低类的复杂度,提高类的可读性、可维护性。
单一职责举例:
在MyBatis的mapper文件中经常可以看到通用型的update方法:
public int update(Student student);
对应的xml文件往往是:
<update id="update">
UPDATE student SET
name = #{name},
age = #{age},
gender = #{gender},
phone = #{phone},
class = #{class},
type = #{type},
update_by = #{updateBy.id},
update_date = #{updateDate},
remarks = #{remarks}
WHERE id = #{id}
</update>
无论要修改Student的那个属性,所有属性都要修改一遍,如果要修改name,就要写一个单一职责的方法:
public int update(@Param("name") String name,@Param("id") String id);
<update id="update">
UPDATE student SET
name = #{name}
WHERE id = #{id}
</update>
迪米特法则
定义:又叫做最少知道原则。一个对象应该对其他对象保持最少的了解。
强调只和朋友交流,不和陌生人说话。
出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。
优点:降低耦合度。
迪米特法则举例:
Java中有PO/Entity: persistent object持久对象、VO:view object表现层对象、DTO:data transfer object,数据传输对象、DAO :data access object数据访问对象、POJO :plain ordinary java object用这个名字用来强调它是一个普通java对象,而不是一个特殊的对象。
它们有时可以通用,各自有各自的生命周期作用范围。
在实际的开发中,经常可以看到Entity从持久层一直传送到Controller层,违反了迪米特法则,Controller、Service、Dao层每一层应该仅和自己的“朋友”打交道。
接口隔离原则
定义:client不应该依赖它不需要的接口。接口依赖遵循最少依赖原则。尽量细化接口。
优点:使得类具有好的可读性、可扩展性、可维护性。
里氏替换原则
定义:如果将程序中的对象A替换为对象B,程序的行为没有发生变化,那么B就是A的子类型。
所有引用父类的地方必须能够透明的使用其字类的对象,字类对象能够替换父类对象, 同时程序逻辑不变。
子类可以扩展父类的功能,但不能改变父类原有功能。
子类可以实现父类的abstract方法,但不能覆盖父类的非abstract方法。子类可以增加自己特有的方法。
子类重载父类的方法时,方法的入参要比父类的方法的入参更宽松。方法的出参要比父类的方法出参更严格或一样。
优点:提高程序健壮性。提高可维护性、扩展性。
上一篇: 12种设计模式C++源代码