浅谈设计模式

2,622 阅读7分钟

在面向对象设计中,首先有这几大原则作为前提,然后在此基础之上有了各类设计模式。

开闭原则

开闭原则(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即可,不同的策略间应该满足隔离和单一职责原则,相互间独立而不耦合。延续刚才的例子,当一个服务需要根据输入参数的不同,去调用不同的服务时,策略模式就非常合适了。

策略模式类图

strategy

定义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();
}

装饰器模式

screen

装饰器模式主要的作用是为现有的类增加新的功能,其使用和实现和代理模式很像,主要的区别在于控制权的转移,代理模式强调访问必须经由代理,而装饰器模式并无此约定。我们经常用到的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();
}

观察者模式

observer

观察者模式在设计中非常常见,系统层面像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();
}