设计模式是在软件开发过程中解决反复出现设计问题的通用方法。它们就像根据需求可调节的预制蓝图,有助于在不同场景中提高代码的灵活性和复用性。

什么是设计模式?

设计模式的概念源自克里斯托佛·亚历山大在《建筑模式语言》一书中的提议。亚历山大认为,建筑设计有一套通用的“模式语言”,包括城市设计、建筑物结构、窗户位置等。随后,埃里希·伽玛、约翰·弗利赛德斯、拉尔夫·约翰逊和理查德·赫尔姆将这一理念引入软件开发,1994 年出版的《设计模式:可复用面向对象软件的基础》一书收录了 23 种适用于面向对象编程的设计模式,被誉为“四人组(Gang of Four, GoF)”著作,成为软件设计领域的经典。

设计模式的复杂性和应用范围不同,其中一些模式只适用于特定的编程语言或技术,而另一些模式则通用性更强,适合应用于整个软件架构。设计模式按照目的主要分为以下三类:

  1. 创建型模式:用于高效、灵活地创建对象。
  2. 结构型模式:帮助组装和优化对象和类的结构。
  3. 行为型模式:优化对象间的通信和任务分配。

设计模式有 6 大原则,经典的 23 种设计模式目的就是为了使软件系统能达到这些原则。

【提示】有需要《设计模式:可复用面向对象软件的基础》pdf 电子书的可以点击链接下载: 设计模式:可复用面向对象软件的基础.pdf (访问密码: 2208)

设计模式的六大原则

1. 设计模式六大原则:开闭原则(OCP)

开闭原则强调“对扩展开放,对修改关闭”,即系统在无需修改现有代码的前提下即可扩展。这个原则通过“抽象约束、封装变化”实现,确保软件架构的稳定性。

示例:假设一个程序需要计算不同几何图形的面积。下方代码展示了如何在支持新的图形类型时避免修改现有代码:

type Areaer interface {
    Area() float64
}

type Rectangle struct {
    Height float64
    Width  float64
}

func (r Rectangle) Area() float64 {
    return r.Height * r.Width
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pow(c.Radius, 2) * math.Pi
}

func calcArea(a Areaer) float64 {
    return a.Area()
}

在这种设计中,calcArea 函数无需修改即可支持新的形状类型,因为每种形状都实现了 Areaer 接口。这种设计既降低了代码耦合,也提高了扩展性。

2. 设计模式六大原则:里氏替换原则(LSP)

里氏替换原则规定子类可以扩展父类的功能,但不能修改父类的已有行为。满足该原则可确保程序在使用子类替代父类时不会出现运行异常。

里氏替换原则关键点

  • 子类只能实现父类的抽象方法,不能覆盖非抽象方法。
  • 子类可添加新的特有方法,但在重写父类方法时,应保证前置条件(输入参数)比父类宽松,后置条件(返回值)更严格或一致。

违背里氏替换原则可能导致程序出现运行错误,特别是在依赖继承关系的情况下。避免这种情况的关键是慎用继承关系,尽可能以接口代替具体实现,确保灵活性和稳定性。

3. 设计模式六大原则:依赖倒置原则(DIP)

依赖倒置原则提倡“面向接口编程,不要面向实现编程”,即高层模块和低层模块都应依赖于抽象,而非具体实现。这一原则使代码更适应于变化,便于扩展和维护。

在代码设计中,可以通过接口或抽象类来声明变量、方法返回类型等,避免直接引用具体类,从而降低类间耦合性,提升系统稳定性。

4. 设计模式六大原则:单一职责原则(SRP)

单一职责原则强调每个类只应承担一个职责,以降低复杂度和提高可维护性。如果一个类承担了多个职责,可能导致变更的耦合,使得代码难以理解。

示例:下方代码演示了如何将职责划分到不同的接口中,避免让 UserService 承担过多责任:

type UserService interface {
    Login()
    Register()
}

type LogService interface {
    LogError()
}

type EmailService interface {
    SendEmail()
}

这样,系统可以灵活应对不同模块的变动而无需修改其他模块的实现,确保代码简洁、清晰、低耦合。

5. 设计模式六大原则:最少知道原则(LKP)

最少知道原则,即迪米特法则,提倡类尽量减少与其他类的交互,保持高内聚和低耦合。类间不应直接调用,最好通过中介或第三方转发调用。避免滥用迪米特法则可以确保系统结构清晰、通信效率合理。

要实现最少知道原则,应注意以下几点:

  1. 在类的设计中尽量减少依赖关系。
  2. 控制类成员访问权限,限制暴露不必要的属性。
  3. 避免滥用 Serializable,控制对外交互接口数量。

6. 设计模式六大原则:接口分离原则(ISP)

接口隔离原则要求尽量将庞大的接口拆分为多个小型接口,减少客户端依赖不必要的功能。这种划分降低了耦合度,提高了系统的灵活性。

接口划分应当兼顾以下几点:

  • 尽量小而适度,避免接口过于复杂。
  • 提高内聚性,减少对外依赖。
  • 使用专门接口处理各类职责。

设计模式应用的挑战与误区

语言特性缺陷

在某些编程语言中,设计模式弥补了语言本身的不足。例如,在缺乏函数式编程支持的语言中,策略模式等设计模式成为了解决特定问题的替代方案。

滥用设计模式

如果你只有一把铁锤,那么任何东西看上去都像是钉子。

设计模式的滥用通常见于初学者,他们在任何地方都使用设计模式,而忽略了实际需求的简单性和特定场景的适用性。这种“铁锤效应”不仅会增加系统复杂度,还可能引发性能问题。


也可以看看