浅谈设计原则和设计模式
前言
设计原则和设计模式旨在帮助我们设计出一个可复用、可扩展、可维护的应用.
设计原则:设计OR重构系统的指导方针.
设计模式:解决某类问题性质有效的方法.
设计原则和设计模式要实现的目标是:在需求变动或者系统升级时,尽可能少的改变代码,尽可能多的实现新的功能.
设计原则是设计模式的"背后的故事",要深入理解设计模式必先深入理解设计原则.
设计原则
1.开闭原则(Open Close Principle)
Open for extension, closed for modifications.
对扩展开放,对修改关闭.
可以说开闭原则是所有原则中最核心的原则,它的定义就是可扩展性可维护性最好的阐释.
2.依赖倒置原则(Dependency Inversion Principle)
Depend upon Abstractions. Don’t depend upon concretions.
依赖倒置原则强调面向接口编程,依赖于抽象而非具体.依赖倒置原则和开闭原则是内在相通的,一个强调扩展,一个是实现扩展最常用的机制.
3.接口隔离原则(Interface Segregation Principle)
Many client-specific interfaces are better than one general-purpose interface.
接口隔离原则强调:一个接口对应一个客户,避免用一个接口提供给多个客户提供服务.一个接口对应一个客户,当该客户的需求变动时,只需要变动它所对应的接口.体现了高内聚低耦合的思想.
4.单一职责原则(Single Responsibility Principle)
A class should have only one reason to change.
就一个类而言,应该只有一个引起他变化的原因.
也就是说,不要把变化原因各不相同的职责放在一起,因为不同的变化会影响到不相干的职责。一个类尽量做到了功能单一,单一职能的不是说职能多了我们实现不了,是为了后期的测试维护,每个类的很单一,我们找错误的时候就可以一步到位.添加新的功能的时候,也不用牵扯到很多的类.
5.里氏替换原则(Liskov’s Substitution Principle)
Derived types must be completely substitutable for their base types.
子类型必须能够替换它们的父类型.这个原则是对继承的一个约束.里氏替换原则很好理解,举个例子:mysql的java驱动必须遵守jdbc规范.
引用一个非常好的总结:开闭原则是总纲,它告诉我们对扩展开放,对修改关闭;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们要在设计接口的时候要精简单一;单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系.
在实际开发中,我们应当尽量达到这些原则,而不是随时需要遵循这些原则.任何Java程序都有违反这些指导方针的地方.在合适的时候使用原则可以让我们的程序更富有弹性,但死板固执的应用这些原则会使程序更加复杂,事倍功半.
设计模式
GOF将23设计模式种设计模式分为创建型、结构型、行为型3种类型.下面列出一些常见模式以及它们的应用实例.下面提到的模式实例的实现与书本上有所差距,相比模式的实现,我更关注的是模式的应用场景.
单例模式(Singleton):保证程序中一个类只有一个实例.
public class Runtime { /** * 类加载时初始化实例 */ private static Runtime currentRuntime = new Runtime(); /** * 提供获取实例的方法 */ public static Runtime getRuntime() { return currentRuntime; } /** 私有化构造函数 */ private Runtime() {} // 剩下的方法
工厂模式(Factory /Abstract Factory Pattern):用于封装对象的创建
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化哪一个类.
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类.
个人感觉工厂方法模式和抽象工厂模式目的一样,只是表现形式不同,我也并不喜欢从概念上区分它们.jdk中的java.util.Calendar类就是工厂模式的典型应用:
public static Calendar getInstance(TimeZone zone, Locale aLocale) { return createCalendar(zone, aLocale); } private static Calendar createCalendar(TimeZone zone, Locale aLocale) { Calendar cal = null; String caltype = aLocale.getUnicodeLocaleType("ca"); /** 根据不同条件创建不同的对象,典型的工厂模式应用场景 **/ if (caltype == null) { if ("th".equals(aLocale.getLanguage()) && ("TH".equals(aLocale.getCountry()))) { cal = new BuddhistCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } else if (caltype.equals("japanese")) { cal = new JapaneseImperialCalendar(zone, aLocale); } else if (caltype.equals("buddhist")) { cal = new BuddhistCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } return cal; }
工厂是很有威力的技巧,可以帮助我们针对抽象编程,而不要针对具体类编程.
原型模式(Prototype):通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建更多的同类型的对象.
应用实例:jdk中实现Prototype原型模式的类有很多,所有实现了java.lang.Cloneable接口的类都实现了原型模式.
建造者模式(Builder):封装复杂对象的创建过程.
应用场景:当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;当构造过程必须允许被构造的对象有不同的表示时.
应用实例:java.lang.StringBuffer,java.lang.StringBuilder
结构模式
适配器模式(Adapter):将一个类的接口转换成客户期望的另一个接口.适配器让原本不兼容的类可以合作无间.
应用实例:jdk中java.io.InputStreamReader和java.io.OutputStreamWriter
装饰者模式(Decorator):动态地为一个对象添加一些额外的行为职责,不改变类的源代码,给对象增加新的功能(OCP原则)
应用实例:jdk中java.io.InputStream和java.io.OutputStream的所有子类
桥接模式(Bridge):在软件系统中,某些类型由于自身逻辑,它具有两个或多个维度的变化,使用桥接模式可以应对这种多维度的变化.
桥接模式的意图: 将抽象部分与实现部分分离,使它们都可以独立的变化。
外观模式(Facade):提供一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用.
代理模式(Proxy):代理模式为一个对象提供一个代表(替身),用于控制该对象的访问.
应用实例:java.lang.reflect.Proxy和java.rmi
享元模式(Flyweight): 用共享的技术有效地支持大量细粒度的对象.
应用实例:java.lang.Integer(Boolean,Character,Byte,Short,Long)
行为模式
策略模式(Strategy):定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.
应用实例:java.util.Comparator的compare()
模板模式(Template):定义了算法的步骤,把这些步骤的实现延迟到子类.模板方法模式提供了一种代码复用的重要技巧.
应用实例:java.lang.ClassLoader.loadClass();
观察者模式(Observer):在对象之间定义一对多依赖,当一个对象改变状态,依赖它的对象都会收到通知,并作出相应的操作.
应用实例:javax.servlet.http.HttpSessionBindingListener
状态模式(State):运行对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.
迭代模式(Iterator):提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露其内部的表示.
应用实例:java.util.Iterator
命令模式(Command):将一个请求封装成对象,这可以让你使用不同的请求、队列或者日志请求来参数化其他对象.命令模式也可以支持撤销操作.
应用实例:实现了java.lang.Runnable接口的子类
责任链模式(Chain of responsibility):当需要多个对象都有机会处理请求时可以考虑使用责任链模式.
应用实例: javax.servlet.Filter.doFilter()
解释器模式(Interpreter):Interpreter是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的语法.简单的说,解释器模式是一种简单的语法解释器.
应用实例:java.util.regex.Pattern java.text.Format的子类
中介者模式(Mediator):集中相关对象之间复杂的沟通和控制方式.
应用实例:java.util.Timer中的schelduleXXX()
备忘录模式(Memento):当你需要让对象返回之前的状态时,就是用备忘录模式.
应用实例:java.util.Date java.io.Serializable.在java中,可以考虑用序列化机制保存对象的状态.