设计模式

设计模式

设计模式的六大原则

  1. 开闭原则 对扩展开放,对修改封闭
  2. 里氏代换原则 任何基类可以出现的地方,子类一定可以出现。
  3. 依赖倒转原则 抽象不应该依赖于细节,细节应该依赖于抽象
  4. 接口隔离原则 使用多个专门的接口,而不使用单一的总接口
  5. 迪米特法则,又称最少知道原则(Demeter Principle) 一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
  6. 合成复用原则 尽量使用对象组合,而不是继承来达到复用的目的
  7. 单一职责原则 一个类只负责一个功能领域中的相应职责

设计模式分类

三大类:

创建型模式

单例,工厂(3种),建造者,原型

结构型模式

适配器,桥接,装饰,外观,代理,享元,组合

行为型模式

责任链,命令模式,解释器,迭代器,中介者,备忘录,状态,策略,模板方法,访问者

简单工厂

简单工厂模式 是一个工厂对象根据收到的消息决定要创建的实例的类型。

优点:工厂类中包含了必要的逻辑,根据用户需要实例化对应的类。

缺点:容易违反高内聚低耦合的原则,增加一个类需要修改工厂代码,所有的产品都由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FoodFactory {

public static Food makeFood(String name) {
if (name.equals("noodle")) {
Food noodle = new LanZhouNoodle();
noodle.addSpicy("more");
return noodle;
} else if (name.equals("chicken")) {
Food chicken = new HuangMenChicken();
chicken.addCondiment("potato");
return chicken;
} else {
return null;
}
}
}

工厂方法模式

定义一个创建对象的工厂接口,让子类决定实例化哪个类,将创建工作推迟到子类中。

具体实现 工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。

优点:符合开-闭原则(扩展开放,修改封闭),解决了简单工厂存在的问题,增加一个产品的实现时,不需要修改父类工厂类的逻辑,只要增加一个子类的实现即可。
缺点:一个具体工厂类只能创建一种具体产品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface FoodFactory {
Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {

@Override
public Food makeFood(String name) {
if (name.equals("A")) {
return new ChineseFoodA();
} else if (name.equals("B")) {
return new ChineseFoodB();
} else {
return null;
}
}
}
public class AmericanFoodFactory implements FoodFactory {

@Override
public Food makeFood(String name) {
if (name.equals("A")) {
return new AmericanFoodA();
} else if (name.equals("B")) {
return new AmericanFoodB();
} else {
return null;
}
}
}

抽象工厂模式

区别:普通工厂产出是一个产品(实例),抽象工厂产出是一个抽象(接口)。区别在于,若添加一个新的产品,前者是修改工厂,后者是创建新工厂(符合“闭合原则”)。
概述:创建其他工厂的工厂。

三者关系,简单工厂是对工厂方法的缩减,将具体实现全部集中到工厂类中,抽象工厂是对工厂方法的增强,可以添加多种产品的实现。减少工厂数量,扩展较不方便。

代码参考github

单例模式

只产生一个实例永久驻留,减少资源开销。

使用场景:

确保系统中对应的类只有一个实例存在。

实现:

  • 声明为private来隐藏构造器
  • private static Singleton实例
  • 声明为public来暴露实例获取方法

实现方式:

  1. 懒汉式

    使用static 定义静态成员变量或静态代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
    };
    public static Singleton getInstance() {
    if (instance == null) {
    instance = new Singleton();
    }
    return instance;
    }
    }
    //使用synchronized保证线程安全
    public class Singleton {
    private volatile static Singleton instance;
    private Singleton() {
    };
    public static synchronized Singleton getInstance() {
    if (instance == null) {
    instance = new Singleton();
    }
    return instance;
    }
    }
    //改进型
    public class Singleton {
    private static Singleton instance;
    private Singleton() {
    };
    public static Singleton getInstance() {
    if (instance == null) {
    synchronized(Singleton.class) {
    instance = new Singleton();
    }
    }
    return instance;
    }
    }
    //双重判定
    public class Singleton {
    private static Singleton instance;
    private Singleton() {
    };
    public static Singleton getInstance() {
    if (instance == null) {
    synchronized(Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }
  2. 饿汉式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {
    };
    public static Singleton getInstance() {
    return instance;
    }
    }
    public class Singleton {
    private Singleton instance = null;
    //静态代码块
    static {
    instance = new Singleton();
    }
    private Singleton (){}
    public static Singleton getInstance() {
    return this.instance;
    }
    }

Initialization Demand Holder (IoDH)模式

1
2
3
4
5
6
7
8
9
10
11
12
13
//Initialization on Demand Holder  
class Singleton {
private Singleton() {
}

private static class HolderClass {
private final static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return HolderClass.instance;
}
}

在IoDH中,我们在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用,由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。

原型模式

原理:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Customer implements Serializable,Cloneable {
private static final long serialVersionUID = -8836367807392087516L;
private String address;

public void setAddress(String address) {
this.address = address;
}

public String getAddress() {
return address;
}
//浅拷贝
@Override
protected Customer clone() throws CloneNotSupportedException {
return (Customer)super.clone();
}
//深拷贝
protected Customer deepClone() throws IOException, ClassNotFoundException {
//序列化输出
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//反序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Customer) objectInputStream.readObject();
}
}

建造者模式

建造者模式的核心在于如何一步步构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品,在软件开发中,如果我们需要创建复杂对象并希望系统具备很好的灵活性和可扩展性可以考虑使用建造者模式。

代码参考github

适配器模式

可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口和抽象目标类接口。

1
2
3
4
5
6
7
8
9
10
11
class Adapter extends Target {  
private Adaptee adaptee; //维持一个对适配者对象的引用

public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}

public void request() {
adaptee.specificRequest(); //转发调用
}
}

适配器继承或依赖已有的对象,实现想要的目标接口。

个人理解:继承老的接口,并重写调用方法,在调用方法中调用新增的接口

桥接模式

将抽象部分与实现部分分离,使它们都可以独立的变化。

个人理解:一个抽象类有一个接口类对象,和一个抽象方法,都有各自不同的实现,在使用时将不同的对象和不同方法实现之间解耦

代码参考github

组合模式

1、组合模式,就是在一个对象中包含其他对象,这些被包含的对象可能是终点对象(不再包含别的对象),也有可能是非终点对象(其内部还包含其他对象,或叫组对象),我们将对象称为节点,即一个根节点包含许多子节点,这些子节点有的不再包含子节点,而有的仍然包含子节点,以此类推。

2、所谓组合模式,其实说的是对象包含对象的问题,通过组合的方式(在对象内部引用对象)来进行布局,我认为这种组合是区别于继承的,而另一层含义是指树形结构子节点的抽象(将叶子节点与数枝节点抽象为子节点),区别于普通的分别定义叶子节点与数枝节点的方式。

它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.ArrayList;
import java.util.List;

public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;

//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}

public void add(Employee e) {
subordinates.add(e);
}

public void remove(Employee e) {
subordinates.remove(e);
}

public List<Employee> getSubordinates(){
return subordinates;
}

public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!