讲一讲在工作中用到的面向对象设计模式,供大家参考,之后也会继续更新。
面向对象设计(OOD)的七大原则
在面向对象设计中,首先有这几大原则作为前提,然后在此基础之上有了各类设计模式。
开闭原则
开闭原则(Open-Closed Principle, OCP), 一个软件实体应该对扩展开放,对修改关闭。即软件实体应该在尽量不修改代码的情况下进行扩展。在该原则中,软件实体可以指一个软件模块,一个由多个类组成的局部结构或者一个独立的类。
开闭原则是面向对象的可复用设计的第一块基石,他是最重要的设计原则。为了满足开闭原则,需要对系统进行抽象化设计,抽象是开闭原则的关键。
里式替换原则
Liskov Substitution Principle(LSP),里式替换原则,该原则规定“子类必须能够替换其父类,否则不应当设计为其子类”。换句话说,父类出现的地方,都应该能够由其子类代替。所以子类只能去扩展基类,而不是隐藏或则覆盖基类。
依赖倒置原则
Dependence Iversion Principle(DIP), 它讲的是“设计和实现要依赖于抽象而非实体”。一方面抽象化更符合人的思维习惯;另一方面,根据里式替换原则,可以很容易将抽象替换为扩展后的具体,这样可以很好的支持开闭原则。
这里可以看到,在公司的Spring项目开发中,各个层次间通过Interface进行依赖,也就是基于此原则。
接口隔离原则
Interface Segration Principle(ISP),“将大的接口打散成多个小的独立的接口”。由于Java类支持实现多个接口,可以很容易的让类具有多种接口的特征,同时每个类可以选择性地只实现目标接口。
单一职责原则
Single Responsibility Principle(SRP),单一职责原则。它讲的是不要存在多于一个导致类变更的原因,是高内聚,低耦合的一个体现。
迪米特法则
Law of Demeter or Least Knowledge Principle(LoD or LKP),“一个对象就尽可能少的了解其它对象”,从而实现松耦合。如果一个类的职责过多,由于多个职责耦合在了一起,任何一个职责的变更都可能引起其它职责的问题,严重影响代码的可维护性和可重用性。
该法则实际上和单一职责原则很类似,强调的高内聚,低耦合。
合成/聚合复用原则
Composite/Aggregate Reuse Principle(CARP/CRP),合成/聚合复用原则。如果新对象的某些功能在别的已经创建好的对象里面已经实现,那么应当尽量使用别的对象提供的功能,使之成为新对象的一部分,而不要在重新创建。新对象可以通过向这些对象的委派达到复用已有功能的效果。简而言之,尽量使用合成/聚合,而非使用继承。
这个原则乍一看和迪米特法则相悖,但我的理解是,可以复用的功能应该是有好的抽象得到,而不是胡乱的复用,这里面其实都是考验工程师功力的地方。
关于静态方法
静态方法实际是无状态且面向方法的设计,适合用作工具类,比如Guava,而不适合用在对象中。不过在Java8中已经支持了interface中的静态方法,也应该是作为实现类需要的工具方法存在。
常用的设计模式
Builder模式
Builder模式非常适用于需要多个构造参数的场景,相比于new一个对象,然后用一堆setter去赋值的方式,这种模式的可读性显然要强很多。在我的项目中,需要调用其它系统的Rest服务,在拼接URL时,用这种模式就非常方便。
基类
/** * 请求构造器基本类 * Created by huanghuan on 2016/12/10. */ public abstract class AbstractURLBuilder { protected String params = ""; protected String prefix = ""; protected void appendParams(String name, String param) { if (name == null || param == null) { return; } if (params.length() == 0) { params += name + "=" + param; } else { params += "&" + name + "=" + param; } } /** * 生成最后查询用的URL地址 */ public abstract String build(); /** * 需要设置请求的前缀,比如http://address/service? * * @param prefix 前缀 */ public AbstractQueryBuilder(String prefix) { this.prefix = prefix; } }
其中包含了基本的prefix前缀和params参数。
实现类
/** * 语音地址 * Created by huanghuan on 2016/12/14. */ public class VoiceBuilder extends AbstractURLBuilder { public VoiceBuilder(String prefix) { super(prefix); } public VoiceBuilder rate(String rate) { appendParams("rate", rate); return this; } public VoiceBuilder le(String le) { appendParams("le", le); return this; } public VoiceBuilder audio(String audio) { appendParams("audio", audio); return this; } @Override public String build() { return prefix + params; } }
这里定义一个语音查询服务的Builder,实际上在build()中,根据需求还可以设置一些默认参数,所以才基类中将其定义为虚方法。
使用
public static void main(String args[]){ String voiceURL=new VoiceBuilder("http://address/service?") .rate("whatever").le("EN").audio("good").build(); }
是不是很简洁且方便。
策略模式和简单工厂模式
策略模式在各类系统中都很常见,其实现也很简单,定义好interface即可,不同的策略间应该满足隔离和单一职责原则,相互间独立而不耦合。延续刚才的例子,当一个服务需要根据输入参数的不同,去调用不同的服务时,策略模式就非常合适了。
策略模式类图
定义Interface
/** * Api调用策略接口 * Created by huanghuan on 2016/12/17. */ public interface ApiStrategy { /** * 获取api调用结果 * * @param requestParams 请求参数 * @return api的查询结果 */ @NotNull JSONObject getApiRequest(RequestParams requestParams) throws ApiException; }
定义实现策略
public class DemoStrategy implements ApiStrategy { @NotNull public JSONObject getApiRequest(RequestParams requestParams) throws ApiException { /** 实现 */ } }
定义工厂
/** * 策略模式管理者,决定Api的调用策略 * Created by huanghuan on 2016/12/17. */ public class ApiStrategyFactory { /** * 决定api的实际调用策略 * * @param requestParams 请求参数 * @return 具体的api调用策略 */ public ApiStrategy decideStrategy(RequestParams requestParams) throws ApiException { /** 根据requestParams决定调用的策略 */ if (true) { return new DemoStrategy(); } else { return whatever; } } }
使用
public static void main(String args[]){ RequestParams req=new RequestParams(); new ApiStrategyFactory().decideStrategy(req).getApiResult(); }
这样的策略之后添加修改策略都会很方便,当然简单工厂方法不满足开闭条件,适用于策略较少的场景。
责任链模式
当需要依次处理一个任务时,可以采用责任链模式,这个模式可实际应用在参数校验,或则流程处理中(比如SpringMVC的9大handler,也就是用类似的模式依次产生作用)
定义基类
public abstract class Handler { protected Handler nextHandler; public abstract void handle(); public Handler getNextHandler() { return nextHandler; } public void setNextHandler(Handler handler) { this.nextHandler = handler; } }
定义子类
public class Father extends Handler { @Override public void handle() { System.out.println("father is called"); if (nextHandler != null) { getNextHandler().handle(); } } } public class Son extends Handler { @Override public void handle() { System.out.println("son is called"); if (nextHandler != null) { getNextHandler().handle(); } } } public class GrandSon extends Handler { @Override public void handle() { System.out.println("grand son is called"); if (nextHandler != null) { getNextHandler().handle(); } } }
使用
public static void main(String[] args) { Father father = new Father(); Son son = new Son(); GrandSon grandSon = new GrandSon(); father.setNextHandler(son); son.setNextHandler(grandSon); father.handle(); }
装饰器模式
装饰器模式主要的作用是为现有的类增加新的功能,其使用和实现和代理模式很像,主要的区别在于控制权的转移,代理模式强调访问必须经由代理,而装饰器模式并无此约定。我们经常用到的Java IO类,便是装饰器模式的实现。
基类
public abstract class Component{ public abstract void operation(); } public abstract class Decorator extends Component{ protected Component component; public Decorator(Component component){ this.component=component; } public abstract void operation(); }
实现类
public class ConcreteComponent extends Component{ public void operation(){ System.out.println("## doing something ##"); } } public class ConcreteDecorator extends Decorator{ public ConcreteDecorator(Component component){ super(component); } public void decorateMethod(){ System.out.println("## decorating ##"); } @Override public void operation(){ decorateMethod(); component.operation(); } }
使用
public static void main(String[] args) { Component component=new ConcreteDecorator(new ConcreteComponent()); component.operation(); }
观察者模式
观察者模式在设计中非常常见,系统层面像Zookeeper,JQuery事件等多是使用的该模式。
主题
public interface Subject { List<Observer> observers = new ArrayList<Observer>(); void attach(Observer observer); void detach(Observer observer); void notifyObserver(); } public static class ConcreteSubject implements Subject { public void attach(Observer observer) { observers.add(observer); } public void detach(Observer observer) { Iterator<Observer> iterator = observers.iterator(); while (iterator.hasNext()) { Observer observerTemp = iterator.next(); if (observerTemp.equals(observer)) { iterator.remove(); } } } public void notifyObserver() { for (Observer observerTemp : observers) { observerTemp.printMessage("this is a test"); } } }
观察者
public interface Observer { void printMessage(String message); } @Data public static class ConcreteObserver implements Observer { private String name; public ConcreteObserver(String name) { this.name = name; } public void printMessage(String message) { System.out.println(name + " " + message); } }
使用
public static void main(String[] args) { Subject subject = new ConcreteSubject(); Observer observer1 = new ConcreteObserver("one"); Observer observer2 = new ConcreteObserver("two"); subject.attach(observer1); subject.attach(observer2); subject.notifyObserver(); subject.detach(observer1); subject.notifyObserver(); }
本文转至:http://sadwxqezc.github.io/HuangHuanBlog/design/2017/01/23/%E6%B5%85%E8%B0%88%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html