在面向对象设计中,首先有这几大原则作为前提,然后在此基础之上有了各类设计模式。
开闭原则
开闭原则(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();
}