目录
设计模式
面向对象
设计模式的六大原则
创建型模式
1、工厂模式
工厂方法(Factory method pattern)
简单工厂
2、抽象工厂(Abstract factory pattern)
3、单例模式(Singleton pattern)
4、建造者模式(Builder Pattern)
5、原型模式(Prototype pattern)
维基百科把设计模式分为了设计模式和软件设计模式两个WIKI,其中前者指的是GOF的设计模式,GOF设计模式针对的是C++和smalltalk等面向对象的高级语言,而软件设计模式是更大的概念,包含了更多模式,实际上后者中很多模式在Java也有广泛的应用,这里只谈GOF的23种设计模式。
面向对象是对世界的抽象,一切皆对象,这种高级语言本身就是一种最顶层的设计。理论上讲,基于这种语言开发系统时搞清需求、设计业务流程图、设计业务实体,然后把这些实体转化成类,一个个用代码实现系统就做完了,不需要什么设计模式,早期一些小系统也就是这么做的。
然后事实却不尽如人意,原因是系统越来越复杂,需要支撑的功能越来越多,对易维护、易拓展的要求越来越高。应用不再单单跟自己打交道,应用之间往往需要相互打交道,构成庞大的系统,外部的不确定性对应用构成挑战。系统的硬件资源要求软件与之配合。
越来越多的因素要求软件要有一定的设计,最好能应付所有的问题。在越来越多的实践上,人们总结出些经验,这些经验有很多是用失败换来的。
可以说六大设计原则是设计最浓缩的精华,也是对设计模式的要求。在遵循设计原则的基础上,会不自知的使用设计模式,哪怕你还不了解设计模式。
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。
在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”
在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象。工厂是构造方法的抽象,用来实现不同的分配方案。工厂对象通常包含一个或多个方法,用来创建这个工厂所能创建的各种类型的对象。这些方法可能接收参数,用来指定对象创建的方式,最后返回创建的对象。
有时,特定类型对象的控制过程比简单地创建一个对象更复杂。在这种情况下,工厂对象就派上用场了。工厂对象可能会动态地创建产品对象的类,或者从对象池中返回一个对象,或者对所创建的对象进行复杂的配置,或者应用其他的操作。
UML用例图
代码举例
例如,有一个Button
类表示按钮,另有它的两个子类WinButton
和MacButton
分别代表Windows和Mac风格的按钮,那么这几个类和用于创建它们的工厂类在Java中可以如下实现(在此省略所有类和方法的可见性设置):
//幾個Button類
class Button{/* ...*/}
class WinButton extends Button{/* ...*/}
class MacButton extends Button{/* ...*/}
//他們的工廠類
interface ButtonFactory{
abstract Button createButton();
}
class WinButtonFactory implements ButtonFactory{
Button createButton(){
return new WinButton();
}
}
class MacButtonFactory implements ButtonFactory{
Button createButton(){
return new MacButton();
}
}
普通的工厂方法模式通常伴随着对象的具体类型与工厂具体类型的一一对应,客户端代码根据需要选择合适的具体类型工厂使用。
然而,这种选择可能包含复杂的逻辑。这时,可以创建一个单一的工厂类,用以包含这种选择逻辑,根据参数的不同选择实现不同的具体对象。这个工厂类不需要由每个具体产品实现一个自己的具体的工厂类,所以可以将工厂方法设置为静态方法。 而且,工厂方法封装了对象的创建过程。
如果创建过程非常复杂(比如依赖于配置文件或用户输入),工厂方法就非常有用了。 比如,一个程序要读取图像文件。程序支持多种图像格式,每种格式都有一个对应的ImageReader
类用来读取图像。程序每次读取图像时,需要基于文件信息创建合适类型的ImageReader
。这个选择逻辑可以包装在一个简单工厂中:
代码举例
public class ImageReaderFactory {
public static ImageReader imageReaderFactoryMethod(InputStream is) {
ImageReader product = null;
int imageType = determineImageType(is);
switch (imageType) {
case ImageReaderFactory.GIF:
product = new GifReader(is);
case ImageReaderFactory.JPEG:
product = new JpegReader(is);
//...
}
return product;
}
}
抽象工厂是“工厂方法”的进一步升级,每个工厂生产一组产品,或者说一个产品的一组组件。
形式上工厂方法是抽象工厂的只有单一产品的特例,但概念上抽象工厂与工厂方法不同,抽象解决的是“同一种产品”的各个组件的变化性,这种变化往往是业务的需求,进而演化出不同的系列产品。参考thinkpad的T系列,E系列,他们拥有相同的键盘(thinkpad的键盘真的太好使了),但是屏幕、A面材质可能不同。
UML用例图
代码举例
//产品接口
public interface Button {}
public interface Border {}
// 实现抽象类
public class MacButton implements Button {}
public class MacBorder implements Border {}
public class WinButton implements Button {}
public class WinBorder implements Border {}
// 接着实现工厂
public class MacFactory {
public static Button createButton() {
return new MacButton();
}
public static Border createBorder() {
return new MacBorder();
}
}
public class WinFactory {
public static Button createButton() {
return new WinButton();
}
public static Border createBorder() {
return new WinBorder();
}
}
单例模式,也叫单子模式,是一种常用的软件设计模式。
在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
代码举例
通常单例模式在Java中,有两种构建方式:
单例模式(饿汉模式)应用的例子如下述代码所示:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// Private constructor suppresses
private Singleton() {}
// default public constructor
public static Singleton getInstance() {
return INSTANCE;
}
}
单例模式(懒汉模式)应用的例子如下述代码所示 (此种方法只能用在JDK5及以后版本(注意 INSTANCE 被声明为 volatile),之前的版本使用“双重检查锁”会发生非预期行为:
public class Singleton {
private static volatile Singleton INSTANCE = null;
// Private constructor suppresses
// default public constructor
private Singleton() {}
//thread safe and performance promote
public static Singleton getInstance() {
if(INSTANCE == null){
synchronized(Singleton.class){
//when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
在以下情况使用生成器模式:
参与者
为创建一个Product对象的各个部件指定抽象接口。
实现Builder的接口以构造和装配该产品的各个部件。
定义并明确它所创建的表示。
提供一个检索产品的接口
构造一个使用Builder接口的对象。
表示被构造的复杂对象。ConcreateBuilder创建该产品的内部表示并定义它的装配过程。
包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
UML用例图
代码举例
/**
* Represents the product created by the builder.
*/
class Car {
private int wheels;
private String color;
public Car() {
}
public String getColor() {
return color;
}
public void setColor(final String color) {
this.color = color;
}
public int getWheels() {
return wheels;
}
public void setWheels(final int wheels) {
this.wheels = wheels;
}
@Override
public String toString() {
return "Car [wheels = " + wheels + ", color = " + color + "]";
}
}
/**
* The builder abstraction.
*/
interface CarBuilder {
Car build();
CarBuilder setColor(final String color);
CarBuilder setWheels(final int wheels);
}
class CarBuilderImpl implements CarBuilder {
private Car car;
public CarBuilderImpl() {
car = new Car();
}
@Override
public Car build() {
return car;
}
@Override
public CarBuilder setColor(final String color) {
car.setColor(color);
return this;
}
@Override
public CarBuilder setWheels(final int wheels) {
car.setWheels(wheels);
return this;
}
}
public class CarBuildDirector {
private CarBuilder builder;
public CarBuildDirector(final CarBuilder builder) {
this.builder = builder;
}
public Car construct() {
return builder.setWheels(4)
.setColor("Red")
.build();
}
public static void main(final String[] arguments) {
final CarBuilder builder = new CarBuilderImpl();
final CarBuildDirector carBuildDirector = new CarBuildDirector(builder);
System.out.println(carBuildDirector.construct());
}
}
原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。
原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效;或者创建值相等,只是命名不一样的同类数据。
UML
代码举例(浅复制)(更多是使用序列化和反序列化实现深复制,这个要看需求)
// Prototype pattern
public abstract class Prototype implements Cloneable {
public Prototype clone() throws CloneNotSupportedException{
return (Prototype) super.clone();
}
}
public class ConcretePrototype1 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype1)super.clone();
}
}
public class ConcretePrototype2 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype2)super.clone();
}
}