Fork me on GitHub

设计模式

十月间断的学了一些设计模式。这是个长久的课程,还没有学会怎么实际运用到项目中。

跟随着视频来学。其实理解最快的方式是看类图。

为什么学习设计模式?

为了让程序具有更好的代码重用性,可读性,可扩展性,可靠性,使程序呈现高内聚,低耦合的特性。

什么时候用到设计模式?

面向对象(oo)=> 功能模块[设计模式 + 算法(数据结构)] => 框架[ 多种设计模式 ] => 架构[ 服务器集群 ]

学习步骤

应用场景 –> 设计模式 –> 剖析原理 –> 分析实现步骤(图解,类图) –> 代码实现 –> 框架或项目源码分析的步骤讲解

设计模式的七大原则

设计模式为什么这样设计的原则。

字符流会查码表,字节流不查码表

编码:字符—>码表—>数字

解码:数字—>码表—>字符

Reader默认查询的码表是与操作系统一致的码表,操作系统是中文的,所以reader使用GBK码表

java查的是unicode的码表

1. 单一职责原则

一个类只负责一个职责。

2. 接口隔离原则

使用多个单独的接口。

3. 依赖倒转原则

其核心是面向接口编程(即抽象类)。

4. 迪米特法则(最少知道原则)

体现了封装的思想。可以适当违反。

5. 开闭原则OCP

对扩展开放,对修改关闭。

6. 里氏替换原则

oo中继承的思考。

所有引用基类的地方必须能透明的使用其子类的对象。

使用继承时尽量不要重写父类的方法。

创建一个更加基础的类。使用依赖,聚合,组合的关系。

7. 合成复用原则

尽量使用合成聚合的方式,而不是使用继承。

UML语言介绍

统一建模语言。

类图-依赖关系(Dependence)只要在类中用到了对方,他们就存在依赖关系。

类图-泛化关系(generalization),就是继承关系,是依赖关系的特例。

类图-实现关系(Implement),是依赖关系的特例

1
2
3
4
5
6
public interface PersonService{
public void delete(Interger id);
}
public class PersonServiceBean implements PersonService{
public void delete(Interger id){}
}

类图-关联关系(Association)

类与类之间的联系,是依赖关系的特例。有单向和双向关系。

类图-聚合关系(Aggregation) 整体与部分的关系,整体与部分可以分开。比如Person和IDCard。聚合式关联关系的特例。

类图-组合关系(Composition)也是整体与部分的关系,但是整体与部分不可以分开。例如Person和head。同生共死的关系。

1
2
3
4
5
6
public class Person{
private IDCard card;
private Head head = new head();
}
public class IDCard{}
public class Head{}

创建型模式

单例模式

主要步骤:

1) 构造器私有化 (防止 new )

2) 类的内部创建对象

3) 向外暴露一个静态的公共方法

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton{
//构造器私有化
new private Singleton(){

}
//本类内部创建对象实例。
private final static Singleton instance = new Singleton();
//提供一公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instantce;
}
}

优缺点:写法简单,在类装载的时候完成实例化,避免了线程同步问题,没有达到懒加载的效果。

可用,但会造成内存的浪费。

双重检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton{
private static volatile Singleton instance;//volatile可以实现立刻写到内存中
//构造器私有化
private Singleton(){}

public static synchronized Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}

Double-Check是多线程开发中常使用的。线程安全,延迟加载,效率较高。

静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton{
private static volatile Singleton instance;
private Singleton(){}
//写一静态内部类
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//提供一静态的公有方法,直接返回 SingletonInstance.INSTANCE。
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}

静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。推荐使用。

枚举法

1
2
3
4
5
6
enum Singleton{
INSTANCE;
public void sayOK(){
System.out.println("ok");
}
}

借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式.推荐使用

简单工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
class FoodFactory{
public static Food getFood(int n){
Food food = null;
switch(n){
case 1:
food = new Hamburger();
break;
case 2:
food = new RiceNoodle();
break;
}
return food;
}
}
...

服务器修改了具体产品的类名后,客户端不知道。

工厂的实现不易于扩展,违反了ocp原则,故引入了工厂方法模式。

工厂方法模式

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
interface food{
void eat();
}

class Hamburger implements Food{
public void eat(){
System.out.println("吃汉堡!");
}
}
class RiceNoodle implements Food{
public void eat(){
System.out.println("吃米线!");
}
}
//食物工厂
interface FoodFactory{
public Food getFood(){}
}

class HamburgerFactory implements FoodFactory{
public Food getFood(){
return new Hamburger();
}
}
class RiceNoodleFactory implements FoodFactory{
public Food getFood(){
return new RiceNoodle();
}
}

public static void main(){
FoodFactory ff = new HamburgerFactory();
Food food = ff.getFood();
food.eat();
}

工厂的名字是视为接口的,是趋向稳定的。

但是当种类很多时,会造成类爆炸式增长,于是引入抽象工厂。

抽象工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Factory{
public Food getFood(){}
public Drink getDrink(){}

}
class KFCFactory implements Factory{
public Food getFood(){
return new Hamburger();
}
public Drink getDrink(){
return new Cola();
}
}
class SanQinFactory implements Factory{
...
}

无论有多少产品等级,工厂就一类。

冰箱电视洗衣机视为产品等级,工厂厂家海尔海信华为系列产品视为产品簇。虽然新增一个工厂很便捷,但抽象工厂扩展产品等级需要所有的工厂都修改,用Spring框架解决。

抽象工厂中可以生产多个产品,这多个产品之间必有内在联系。同一个工厂中的产品都属于一个产品簇

原型模式

原型模式用于创建重复的对象,同时又能保证性能。

当直接创建对象的代价比较大时,可以采用这种方式。

源码应用有Spring

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 class Sheep implements Cloneable{
private String name;
private int age;
private String color;
//构造器
public Sheep(String name,int age, String color){
this.name = name;
this.age = age;
this.color = color;
}
//提供对外的接口
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
...
//克隆该实例
protected Object clone(){
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();//sheep是object,但是object不一定是sheep
}catch(Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}

Java中的Object类是所有类的根类,Object类提供了一个clone()的方法。需要实现clone的java类必须实现一个接口Cloneable,该接口表示该类能复制且有复制的能力。

上例的clone实现使用了默认的clone(),是浅拷贝,要实现深拷贝可以采用重写clone函数或者序列化

c++可以用拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
class ISplitter{
public:
virtual void split() = 0;
virtual ISplitter* clone() = 0;//纯虚函数
virtual ~ISplitter(){}
}
class BinarySplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new BinarySplitter(*this);
}
}

建造者模式

使用多个简单的对象一步一步构建成一个复杂的对象。

源码应用有StringBuilder

建造产品的过程的产品解耦合?

与工厂模式的区别:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//Product
public class House{
private String baise;
private String wall;
private String roofed;
public String getBaise(){
return baise;
}
public void setBaise(){
this.baise = baise;
}
...
}
//抽象建造者
public abstract class HouseBuilder{
protected House house = new House();

public abstract void buildBasic();
public abstract void buildWall();
public abstract void roofed();

public House buildHouse(){
return house;
}
}

public class CommonHouse extends HouseBuilder{
public void buildBasic(){
System.out.println("普通房子打地基5m");
}
public void buildWall(){
System.out.println("普通房子打砌墙10cm");
}
public void roofed(){
System.out.println("普通房子打顶");
}
}

public class HighHouse extends HouseBuilder{
public void buildBasic(){
System.out.println("高楼打地基100m");
}
public void buildWall(){
System.out.println("高楼打砌墙20cm");
}
public void roofed(){
System.out.println("高楼打顶");
}
}

...//可扩展房子类型

//建造指挥
public class HouseDirector{
HouseBuilder houseBuilder = null;
//构造器传入houseBuilder
public HouseDirector(HouseBuilder houseBuilder){
this.houseBuilder = houseBuilder;
}
//setter传入houseBuilder
public void setHouseBuilder(HouseBuilder houseBuilder){
this.houseBuilder = houseBuilder;
}
public House constructHouse(){
houseBuilder.buildBasic();
houseBuilder.buildWalls();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}
}
//用户
public class Client{
public static void main(){
CommonHouse commonHouse = new CommonHouse();
HouseDirector houseDirector = new HouseDirector(commonHouse);
House house = houseDirector.constructHouse();
}
}

结构型模式

适配器模式

Adapter, 对应源码中的应用有Dispatcher handler

作为两个不兼容的接口之间的桥梁。

举例为电压转换

桥接模式

Bridge,源码应用有JDBC:driver

将抽象化和实现解耦

手机品牌问题

组合模式

Comoposite, 源码应用有HashMap

根据树形结构来组合对象

院校展示问题

装饰模式

Decorator, 源码应用有IO

向现有的对象中添加新的功能

咖啡加料问题

外观模式

Facade, 源码应用有Mybatis

隐藏系统复杂性,向客户端提供一个客户端可以访问的接口

影院仪器管理问题

享元模式

Flyweight, 源码应用有数据库连接池 Integer

重用现有的同类对象

棋盘问题

代理模式

Proxy, 源码应用有AOP

一个类代表另一个类的功能

行为型模式

责任链模式

源码应用:SpringMVC中的ServletFilter, Spring中的SpringInterceptor AOP也通过这一模式来顺序执行每一个通知



本文标题:设计模式

文章作者:tsuki

发布时间:2020.11.08 - 11:42

最后更新:2022.10.13 - 16:36

原始链接:https://tsuki419.github.io/设计模式.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------THE END-------------
0%