参考答案
单一职责原则
一个类,应当只有一个引起它变化的原因;即一个类应该只有一个职责。
就一个类而言,应该只专注于做一件事和仅有一个引起变化的原因,这就是所谓的单一职责原则。该原则提出了对对象职责的一种理想期望,对象不应该承担太多职责,正如人不应该一心分为二用。唯有专注,才能保证对象的高内聚;唯有单一,才能保证对象的细粒度。对象的高内聚与细粒度有利于对象的重用。一个庞大的对象承担了太多的职责,当客户端需要该对象的某一个职责时,就不得不将所有的职责都包含进来,从而造成冗余代码。
里氏替换原则
在面向对象的语言中,继承是必不可少的、优秀的语言机制,它主要有以下几个优点:
相应的,继承也存在缺点,主要体现在以下几个方面:
从整体上看,继承的“利”大于“弊”,然而如何让继承中“利”的因素发挥最大作用,同时减少“弊”所带来的麻烦,这就需要引入“里氏替换原则”。里氏替换原则的定义有以下两种:
依赖倒置原则
依赖倒置原则包括三种含义:
传统的过程性系统的设计办法倾向于高层次的模块依赖于低层次的模块;抽象层次依赖于具体层次。“倒置”原则将这个错误的依赖关系倒置了过来,如下图所示,由此命名为“依赖倒置原则”。
在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是具体的实现类,实现类实现了接口或继承了抽象类,其特点是可以直接被实例化。依赖倒置原则在Java语言中的表现是:
依赖倒置原则更加精确的定义就是“面向接口编程”——OOD(Object-Oriented Design)的精髓之一。依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。依赖倒置原则是JavaBean、EJB和COM等组件设计模型背后的基本原则。
接口隔离原则
接口隔离原则有如下两种定义:
接口隔离原则的具体含义如下:
迪米特法则
迪米特法则又叫最少知识原则,意思是一个对象应当对其他对象尽可能少的了解。迪米特法则不同于其他的OO设计原则,它具有很多种表述方式,其中具有代表性的是以下几种表述:
按照迪米特法则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。
开闭原则
开闭原则的定义是:一个软件实体应当对扩展开放,对修改关闭。这个原则说的是,在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。
在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他原则(单一职责、里氏替换、依赖倒置、接口隔离、迪米特法则)都是开闭原则的具体形态,即其他原则都是开闭原则的手段和工具。开闭原则的重要性可以通过以下几个方面来体现。
参考答案
开闭原则的定义是:一个软件实体应当对扩展开放,对修改关闭。这个原则说的是,在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。
在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他原则(单一职责、里氏替换、依赖倒置、接口隔离、迪米特法则)都是开闭原则的具体形态,即其他原则都是开闭原则的手段和工具。开闭原则的重要性可以通过以下几个方面来体现。
参考答案
饿汉式单例模式:
public class Singleton { private static Singleton instance = new Singleton(); // 私有构造方法,保证外界无法直接实例化。 private Singleton() {} // 通过公有的静态方法获取对象实例 public static Singleton getInstace() {return instance; }
}
懒汉式单例模式:
public class Singleton { private static Singleton instance = null; // 私有构造方法,保证外界无法直接实例化。 private Singleton() {} // 通过公有的静态方法获取对象实例 public static Singleton getInstace() { if (instance == null) { instance = new Singleton(); } return instance; }
}
参考答案
在懒汉式单例模式基础上实现线程同步:
public class Singleton {private static Singleton instance = null;// 私有构造方法,保证外界无法直接实例化。 private Singleton() {}// 通过公有的静态方法获取对象实例 synchronized public static Singleton getInstace() {if (instance == null) {instance = new Singleton();}return instance;}
}
上述代码对静态方法 getInstance()进行同步,以确保多线程环境下只创建一个实例。如果getInstance()方法未被同步,并且线程A和线程B同时调用此方法,则执行if (instance == null)语句时都为真,那么线程A和线程B都会创建一个对象,在内存中就会出现两个对象,这样就违反了单例模式。而使用synchronized关键字进行同步后,则不会出现此种情况。
参考答案
工厂模式的用意是定义一个创建产品对象的工厂接口,将实际创建性工作推迟到子类中。工厂模式可分为简单工厂、工厂方法和抽象工厂模式。注意,我们常说的23种经典设计模式,包含了工程方法模式和抽象工厂模式,而并未包含简单工厂模式。另外,我们平时说的工厂模式,一般默认是指工厂方法模式。
简单工厂
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。简单工厂的适用场景是:
示例:
创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,不看代码先考虑一下如何通过该模式设计完成此功能。
由题可知圆形,正方形,三角形都属于一种图形,并且都具有draw方法,所以首先可以定义一个接口或者抽象类,作为这三个图像的公共父类,并在其中声明一个公共的draw方法:
public interface Shape {void draw();
}下面就是编写具体的图形,每种图形都实现Shape接口:
// 圆形 class CircleShape implements Shape {
public CircleShape() {System.out.println("CircleShape: created");} @Override
public void draw() {System.out.println("draw: CircleShape");}}
// 正方形 class RectShape implements Shape {
public RectShape() {System.out.println("RectShape: created");}
@Override
public void draw() {System.out.println("draw: RectShape");}}
// 三角形 public class TriangleShape implements Shape {
public TriangleShape() {System.out.println("TriangleShape: created");}
@Override
public void draw() {System.out.println("draw: TriangleShape");}}
下面是工厂类的具体实现:
class ShapeFactory { public static Shape getShape(String type) { Shape shape = null; if (type.equalsIgnoreCase("circle")) { shape = new CircleShape(); } else if (type.equalsIgnoreCase("rect")) { shape = new RectShape(); } else if (type.equalsIgnoreCase("triangle")) { shape = new TriangleShape(); } return shape; }
}
为工厂类传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
工厂方法
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。
工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
示例:
现在需要设计一个这样的图片加载类,它具有多个图片加载器,用来加载jpg,png,gif格式的图片,每个加载器都有一个read()方法,用于读取图片。下面我们完成这个图片加载类。
首先完成图片加载器的设计,编写一个加载器的公共接口:
public interface Reader {
void read();
}
然后完成各个图片加载器的代码:
// jpg图片加载器
class JpgReader implements Reader { @Override public void read() { System.out.print("read jpg"); }
}
// png图片加载器
class PngReader implements Reader { @Override public void read() { System.out.print("read png"); }
} // gif图片加class GifReader implements Reader { @Override public void read() { System.out.print("read gif"); }
}
现在我们按照定义所说定义一个抽象的工厂接口ReaderFactory:
interface ReaderFactory { Reader getReader(); }
里面有一个getReader()方法返回我们的Reader 类,接下来我们把上面定义好的每个图片加载器都提供一个工厂类,这些工厂类实现了ReaderFactory 。
在每个工厂类中我们都通过重写的getReader()方法返回各自的图片加载器对象。
和简单工厂对比一下,最根本的区别在于,简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类,这些工厂类都实现了一个工厂基类。
下面总结一下工厂方法的适用场景:
抽象工厂
这个模式最不好理解,而且在实际应用中局限性也蛮大的,因为这个模式并不符合开闭原则。实际开发还需要做好权衡。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。
抽象工厂的实现思路是,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂和工厂方法一样可以划分为4大部分:
示例:
现在需要做一款跨平台的游戏,需要兼容Android,Ios,Wp三个移动操作系统,该游戏针对每个系统都设计了一套操作控制器(OperationController)和界面控制器(UIController),下面通过抽闲工厂方式完成这款游戏的架构设计。
由题可知,游戏里边的各个平台的UIController和OperationController应该是我们最终生产的具体产品。所以新建两个抽象产品接口。
抽象操作控制器:
interface OperationController { void control();}
抽象界面控制器:
interface UIController { void display();
}
然后完成各个系统平台的具体操作控制器和界面控制器。
Android:
class AndroidOperationController implements OperationController {@Overridepublic void control() {System.out.println("AndroidOperationController");}
}
class AndroidUIController implements UIController {@Overridepublic void display() {System.out.println("AndroidInterfaceController");}
}
IOS:
class AndroidOperationController implements OperationController {@Overridepublic void control() {System.out.println("AndroidOperationController");}
}
class AndroidUIController implements UIController {@Overridepublic void display() {System.out.println("AndroidInterfaceController");}
}
WP:
class AndroidOperationController implements OperationController {@Overridepublic void control() {System.out.println("AndroidOperationController");}
}
class AndroidUIController implements UIController {@Overridepublic void display() {System.out.println("AndroidInterfaceController");}
}
下面定义一个抽闲工厂,该工厂需要可以创建OperationController和UIController。
public interface SystemFactory {public OperationController createOperationController();public UIController createInterfaceController();
}
在各平台具体的工厂类中完成操作控制器和界面控制器的创建过程。
Android:
public class AndroidFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new AndroidOperationController();}@Overridepublic UIController createInterfaceController() {return new AndroidUIController();}
}
IOS:
public class IosFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new IosOperationController();}@Overridepublic UIController createInterfaceController() {return new IosUIController();}
}
WP:
public class WpFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new WpOperationController();}@Overridepublic UIController createInterfaceController() {return new WpUIController();}
}
下面总结一下抽象工厂的适用场景:
参考答案
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。抽象工厂的实现思路是,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
参考答案
简单工厂
示例:
创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,不看代码先考虑一下如何通过该模式设计完成此功能。
由题可知圆形,正方形,三角形都属于一种图形,并且都具有draw方法,所以首先可以定义一个接口或者抽象类,作为这三个图像的公共父类,并在其中声明一个公共的draw方法:
public interface Shape {void draw();
}
下面就是编写具体的图形,每种图形都实现Shape接口:
// 圆形
class CircleShape implements Shape {public CircleShape() {System.out.println("CircleShape: created");}@Overridepublic void draw() {System.out.println("draw: CircleShape");}
}
// 正方形
class RectShape implements Shape {public RectShape() {System.out.println("RectShape: created");}@Overridepublic void draw() {System.out.println("draw: RectShape");}}
// 三角形
public class TriangleShape implements Shape {public TriangleShape() {System.out.println("TriangleShape: created");}@Overridepublic void draw() {System.out.println("draw: TriangleShape");}
}
下面是工厂类的具体实现:
class ShapeFactory {public static Shape getShape(String type) {Shape shape = null;if (type.equalsIgnoreCase("circle")) {shape = new CircleShape();} else if (type.equalsIgnoreCase("rect")) {shape = new RectShape();} else if (type.equalsIgnoreCase("triangle")) {shape = new TriangleShape();}return shape;}}
为工厂类传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
工厂方法
示例:
现在需要设计一个这样的图片加载类,它具有多个图片加载器,用来加载jpg,png,gif格式的图片,每个加载器都有一个read()方法,用于读取图片。下面我们完成这个图片加载类。
首先完成图片加载器的设计,编写一个加载器的公共接口:
public interface Reader {void read();
}
然后完成各个图片加载器的代码:
// jpg图片加载器
class JpgReader implements Reader {@Overridepublic void read() {System.out.print("read jpg");}
}
// png图片加载器
class PngReader implements Reader {@Overridepublic void read() {System.out.print("read png");}
}
// gif图片加载器
class GifReader implements Reader {@Overridepublic void read() {System.out.print("read gif");}
}
现在我们按照定义所说定义一个抽象的工厂接口ReaderFactory:
interface ReaderFactory {Reader getReader();
}
里面有一个getReader()方法返回我们的Reader 类,接下来我们把上面定义好的每个图片加载器都提供一个工厂类,这些工厂类实现了ReaderFactory 。
interface ReaderFactory {Reader getReader();
}
在每个工厂类中我们都通过重写的getReader()方法返回各自的图片加载器对象。
抽象工厂
示例:
现在需要做一款跨平台的游戏,需要兼容Android,Ios,Wp三个移动操作系统,该游戏针对每个系统都设计了一套操作控制器(OperationController)和界面控制器(UIController),下面通过抽闲工厂方式完成这款游戏的架构设计。
由题可知,游戏里边的各个平台的UIController和OperationController应该是我们最终生产的具体产品。所以新建两个抽象产品接口。
抽象操作控制器:
interface OperationController {void control();
}
interface UIController {void display();
}
然后完成各个系统平台的具体操作控制器和界面控制器。
Android:
class AndroidOperationController implements OperationController {@Overridepublic void control() {System.out.println("AndroidOperationController");}
}
class AndroidUIController implements UIController {@Overridepublic void display() {System.out.println("AndroidInterfaceController");}
}
IOS:
class IosOperationController implements OperationController {@Overridepublic void control() {System.out.println("IosOperationController");}
}
class IosUIController implements UIController {@Overridepublic void display() {System.out.println("IosInterfaceController");}
}
WP:
class WpOperationController implements OperationController {@Overridepublic void control() {System.out.println("WpOperationController");}
}
class WpUIController implements UIController {@Overridepublic void display() {System.out.println("WpInterfaceController");}
}
下面定义一个抽闲工厂,该工厂需要可以创建OperationController和UIController。
public interface SystemFactory {public OperationController createOperationController();public UIController createInterfaceController();
}
在各平台具体的工厂类中完成操作控制器和界面控制器的创建过程。
Android:
public class AndroidFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new AndroidOperationController();}@Overridepublic UIController createInterfaceController() {return new AndroidUIController();}
}
IOS:
public class IosFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new IosOperationController();}@Overridepublic UIController createInterfaceController() {return new IosUIController();}
}
WP:
public class WpFactory implements SystemFactory {@Overridepublic OperationController createOperationController() {return new WpOperationController();}@Overridepublic UIController createInterfaceController() {return new WpUIController();}
}
参考答案
策略模式(Strategy Pattern)也叫政策模式,是一种比较简单的模式。它的目的是定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换,使得算法可以在不影响到客户端的情况下发生变化。
策略模式的通用类图如下图所示:
策略模式涉及以下3个角色:
抽象策略Strategy的代码如下所示:
public abstract class Strategy { public abstract void strategyInterface();
}
具体策略ConcreteStrategy的代码如下所示:
public class ConcreteStrategy extends Strategy { public void strategyInterface() { ... }
}
环境角色Context的代码如下所示:
public class Context {private Strategy strategy = null;public Context(Strategy strategy) {this.strategy = strategy;}public void contextInterface() {this.strategy.strategyInterface();}
}
策略模式包括如下优点:
策略模式包括如下缺点:
策略模式有如下几个应用场景:
参考答案
观察者模式(Observer Pattern)也称发布订阅模式,它的目的是定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
观察者模式的类图如下图所:
观察者模式具有以下4个角色:
上述类图所涉及的代码如下所示:
interface Subject {// 登记一个新的观察者public void attach(Observer obs);// 删除一个登记过的观察者public void detach(Observer obs);// 通知所有登记过的观察者对象public void notifyObserver();
}
interface Observer {// 更新方法public void update();
}
class ConcreteSubject implements Subject {private Vector obsVector = new Vector();// 登记一个新的观察者public void attach(Observer obs) {obsVector.add(obs);}// 删除一个登记过的观察者public void detach(Observer obs) {obsVector.remove(obs);}// 通知所有登记过的观察者对象public void notifyObserver() {for (Observer e : obsVector) {e.update();}}// 返回观察者集合的Enumeration对象public Enumeration observers() {return obsVector.elements();}// 业务方法,改变状态public void change() {this.notifyObserver();}
}
class ConcreteObserver implements Observer {// 实现更新方法public void update() {System.out.println("收到通知,并进行处理!");}
}
观察者模式具有以下几个优点:
观察模式的缺点如下:
观察者模式的应用场景如下:
参考答案
责任链模式(Chain of Responsibility Pattern)是一种常见的行为模式,它的目的是使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式的重点是在“链”上,由一条链去处理相似的请求,在链中决定谁来处理这个请求,并返回相应的结果。责任链模式的类图如下图所示:
责任链模式涉及以下两个角色:
上述类图所涉及的代码如下所示:
abstract class Handler {private Handler successor;public abstract void handleRequest();public Handler getSuccessor() {return successor;}public void setSuccessor(Handler successor) {this.successor = successor;}
}
class ConcreteHandler extends Handler {// 处理请求public void handleRequest() {if (getSuccessor() != null) {System.out.println("请求传递给" + getSuccessor());getSuccessor().handleRequest();} else {System.out.println("请求处理");}}
}
责任链模式的优点如下:
责任链模式的缺点如下:
责任链模式的应用场景如下:
参考答案
装饰器的目的是动态地给一个对象添加一些额外的职责,这个对象的类型不会发生变化,但是行为却发生了改变。
适配器的目的是将一个类的接口变换成客户端所期待的另一种接口,就是可以将一个对象包装成另外的一个接口。
Spring框架在实现时运用了大量的设计模式,常见的有如下几种: