设计模式 -- 正确学习姿势
1 设计模式:
使用设计模式优化排序工具包的设计
1.1 什么是设计模式?
每一种模式都描述了一种问题的通用解决方案。这种问题在我们的环境中,不停的重复出现。
设计模式是一种可重复使用的解决方案。
典型的重复问题
<===设计模式(问题关注点,如何解决,优缺点,改进点)
<===通用解决方案。
1.2 设计模式学习方法:
1.设计模式解决什么问题?
2.设计模式如何解决问题?
3.设计模式解决过程中,体现的优势什么?如何改进?有没有更好的解决方案呢?
1.3 设计模式的四个组成部分:
1.模式的名称--表达设计
2.待解问题--描述了何时需要运用这种模式,以及运用模式的环境(上下文)
3.解决方案--描述了组成设计的元素(类和对象),他们的关系,职责以及合作,但是这种解决方案是抽象的,他不代表具体的实现。
4.结论--运用这种模式所带来的利弊。主要是指它对系统的弹性,扩展性和可移植性性的影响。
1.4 设计模式分类:
从功能分:
创建模式(Creational Patterns)--对类的实例化过程的抽象。
结构模式(Structural Patterns)--将类或者对象组合形成更大的结构。
行为模式 (Behavioral Patterns) --对在不同的对象之间划分职责和算法的抽象化。
从方式分:
类模式-----以继承的方式实现模式,静态的。
对象模式---以组合的方式实现模式,动态的。
2 案例分析:
设计排序工具包:
1.可以使用各种算法---------------Sorter(冒泡排序,快速排序,插入排序...)使用什么算法排序
2.可以对各种数据结构排序--------Sortable(Array,List.....)数据容器:数据存放到什么位置
3.可以对数据内容排序------------Comparator(integer....)数据内容:数据内容怎么比较
客户端使用:可以直接 new 一个具体的 Sorter。
问题:如果直接 new 一个具体的对象,则客户端程序直接与具体对象绑定,如果要使用不同的算法,则需要更新代码。 客户端需要调整代码,导致客户端的不稳定。====>违反 OCP 原则(开放关闭原则)。
改进目标:让客户端程序依赖抽象编程,不依赖具体类,即使更换具体的排序算法 Sorter(比如:快速排序), 客户端不需要更新代码,保持客户端程序的稳定。
2.1 改进方案一:工厂模式
遵循 OCP 设计原则,使用设计模式(简单工厂)指导:
代码:
public class SorterFactory{
public static <T> Sorter<T> getSorter(){
return new BubbleSorter<T>();//使用新的排序算法,这里不符合开闭原则。
}
}
public class Client{ //符合开闭原则
public static void main(String[] args){
Integer[] array={5,4,9,7,6,3,8,1,0,2};
Sorter<Integer> sorter=SorterFactory.getSorter();
Sortable<Integer> sortable=SortableFactory.getSortable();
Comparator<Integer> comparator=ComparatorFactory.getComparator();
sorter.sort(sortable,comparator);
...
}
}
改进评估:Client 符合 OCP 原则,更新算法时,Client 不需要更新代码,重新编译。
但是 SortFactory,如果使用新的算法时,需要更新 new 一个对象,
来更新代码==>导致 SortFactory 不符合 OCP 原则。
2.2 改进方案二:
改进 SortFactory 符合 OCP, 代码如下:
class SorterFactory_2{
public static <T> Sorter<T> getSorter(String implClass){
try{
Class impl=Class.forName(implClass);
return (Sorter<T>)impl.newInstance();
}catch(Exception e){
throw new IllegalArgumentException("Illegal class name: "+implClass,e);
}
}
}
Sorter<Integer> sorter=SorterFactory_2.getSorter("demo.sort.impl.BubbleSorter");
改进评估:Sorter 排序算法的具体类名,当作字符串传递给工厂方法,工厂方法反射获取实例。
Sorter 如果需要新的算法,只需要传递类的完全限定名,不需要再修改程序本身。
==> SorterFactory 符合 OCP。但是,Client 需要传递算法的完全限定名。
优劣评估:=> 1.Client 代码需要修改,Client 又不符合 OCP;
=> 2.丧失编译器的类型安全检查(Client 和 Factory 均不安全)
=> 3.Client 任然知道 Sorter 的实现是什么
=> 4.限制了 Sorter 的实现只能通过“默认构造函数”创建
2.3 改进方案三:
对 SortFactory 继续改进
class SorterFactory_3{
private final static Properties IMPLS=loadImpls();
private static Properties loadImpls(){
Properties defaultImpls=new Properties();
Properties impls=new Properties(defaultImpls);
defaultImpls.setProperty("sorter","demo.sort.impl.BubbleSorter");
try{
impls.load(SorterFactory_3.class.getResourceAsStream("sort.properties"));
}catch(IOException e){
throw new RuntimeException(e);
}
return impls;
}
public static <T> Sorter<T> getSorter(){
String implClassName=IMPLS.getProperty("sorter");
try{
Class implClass =Class.forName(implClassName);
return (Sorter<T>)implClass.newInstance();
}catch(Exception e){
throw new IllegalArgumentException("Illegal class name:"+implClassName,e);}
}
}
创建 sort.properties 文件: sorter=demo.sort.impl.BubbleSorter
改进评估:不再通过传递参数的方式,而是通过外部配置,加载 Sorter 的具体算法类完全限定名。
如果文件中没有配置,则使用默认配置。
优劣评估:1.如果使用新的排序算法,只需要再文件中配置完全限定名,getSorter()代码不需要再
修改,保持了代码的稳定性。符合 OCP 原则。
2.Client 类不需要再关注算法细节,不需要传递算法的完全限定名。
使用新的算法的时候,Client 不需要修改代码。保持了代码的稳定性。符合 OCP。
3.缺少编译期类型安全。
引申: spring.handler=...........配置?思考?
评论