目录

软件工程6大原则

宗旨:高内聚、低耦合、好扩展、好维护

六大设计原则

  • 单一职责原则(Single Responsibility Principle, SRP)
  • 开放封闭原则(Open-Closed Principle, OCP)
  • 里氏替换原则(Liskov Substitution Principle, LSP)
  • 接口隔离原则(Interface Segregation Principle, ISP)
  • 依赖倒置原则(Dependency Inversion Principle, DIP)
  • 迪米特法则(Law of Demeter, LoD)

单一职责原则(Single Responsibility Principle, SRP)

一个类或函数,只做一件事情

比如👇UserService和Logger都只做一件事,符合SRP

 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
package main

import "fmt"

// User 用户数据结构
type User struct {
    ID   int
    Name string
}

// UserService 仅负责用户信息管理(单一职责)
type UserService struct{}

func (u *UserService) CreateUser(name string) User {
    return User{ID: 1, Name: name}
}

// Logger 仅负责日志记录(单一职责)
type Logger struct{}

func (l *Logger) Log(message string) {
    fmt.Printf("[Log] %s\n", message)
}

func main() {
    userService := UserService{}
    logger := Logger{}
    
    user := userService.CreateUser("Alice")
    logger.Log(fmt.Sprintf("User created: %s", user.Name)) // 职责分离,修改日志逻辑不影响用户管理
}

开放关闭原则(Open-Closed Principle, OCP)

当需要对函数或类A新增功能时,应该在外部写,然后调用新功能,而非在内部写

或称“对调用开放,对修改封闭”

比如PaymentProcessor依赖Payment接口,新增PayPalPayment时只需实现接口,无需修改PaymentProcessor的代码,符合 “对调用(或扩展)开放,对修改关闭”。

 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
package main

import "fmt"

// Payment 支付接口(抽象层)
type Payment interface {
    Pay(amount float64) string
}

// CreditCardPayment 信用卡支付(实现接口)
type CreditCardPayment struct{}

func (c *CreditCardPayment) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

// PayPalPayment PayPal支付(新增实现,扩展功能)
type PayPalPayment struct{}

func (p *PayPalPayment) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

// PaymentProcessor 支付处理器(依赖抽象,对修改关闭)
type PaymentProcessor struct {
    payment Payment
}

func (p *PaymentProcessor) ProcessPayment(amount float64) string {
    return p.payment.Pay(amount) // 无需修改此处即可支持新支付方式
}

func main() {
    // 扩展:新增支付方式时,只需实现Payment接口
    creditCard := &CreditCardPayment{}
    processor := &PaymentProcessor{payment: creditCard}
    fmt.Println(processor.ProcessPayment(100.50))

    paypal := &PayPalPayment{}
    processor = &PaymentProcessor{payment: paypal} // 直接替换支付方式
    fmt.Println(processor.ProcessPayment(200.00))
}

里氏替换原则(Liskov Substitution Principle, LSP)

两个类实现接口,对于所有使用到接口的地方,都能保证执行对应类的逻辑

比如下方Rectangle和Square都实现了Shape接口,在调用PrintArea时,根据传入具体的类的不同,执行了对应的逻辑

 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
package main

import "fmt"

// Shape 图形接口
type Shape interface {
	Area() float64
}

// Rectangle 长方形
// Rectangle 实现了Shape接口
type Rectangle struct {
	Width  float64
	Height float64
}

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

// Square 正方形(是特殊的长方形)
// Square 实现了Shape接口
type Square struct {
	Side float64
}

func (s *Square) Area() float64 {
	return s.Side * s.Side // 符合LSP:替换Shape时面积计算正确
}

// PrintArea 接收Shape接口,可透明替换为任何实现类
func PrintArea(shape Shape) {
	fmt.Printf("Area: %.2f\n", shape.Area())
}

func main() {
	rect := &Rectangle{Width: 3, Height: 4}
	square := &Square{Side: 3}

	PrintArea(rect)   // 输出:Area: 12.00(正确)
	PrintArea(square) // 输出:Area: 9.00(正确,符合LSP)
}

接口隔离原则(Interface Segregation Principle, ISP)

假设有个接口Object,有3个方法,eat,work,sleep

那么Human类可以实现Object,但是Robot类不能实现Object,因为他无法执行eat和sleep方法(不需要吃饭和睡觉)

该怎么解决?

把Object接口拆成3个接口(或2个),Eater、Worker、Sleeper,分别有eat、work、sleep三个方法

让Human类实现Eater、Worker、Sleeper三个接口,让Robot实现Worker一个接口

即某个类不应该有不用的方法

比如👇Human和Robot按需实现接口

 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
package main

import "fmt"

// 拆分后的小接口
type Eater interface {
    Eat() string
}

type Worker interface {
    Work() string
}

type Sleeper interface {
    Sleep() string
}

// Human 实现所有接口(需要吃饭、工作、睡觉)
type Human struct{}

func (h *Human) Eat() string   { return "Human is eating" }
func (h *Human) Work() string  { return "Human is working" }
func (h *Human) Sleep() string { return "Human is sleeping" }

// Robot 只实现Work接口(不需要吃饭、睡觉)
type Robot struct{}

func (r *Robot) Work() string { return "Robot is working" } // 不依赖不需要的Eater和Sleeper

// 客户端函数:只依赖必要接口
func Feed(eater Eater) {
    fmt.Println(eater.Eat())
}

func ManageWorker(worker Worker) {
    fmt.Println(worker.Work())
}

func main() {
    human := &Human{}
    robot := &Robot{}

    Feed(human)        // 正确:Human实现了Eater
    ManageWorker(human) // 正确:Human实现了Worker
    ManageWorker(robot) // 正确:Robot实现了Worker(不依赖多余接口)
}

依赖倒置原则(Dependency Inversion Principle, DIP)

已有包含关系为,A包含B,B包含C,其中A是最高层,C是最底层,那么A就不应该再直接包含C

比如OrderService包含Payment接口,Payment包含CreditCardPayment,所以OrderService不能直接包含CreditCardPayment。当需要更换支付方式时,只需新增类,然后实现Payment接口,无需修改OrderService。

 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
package main

import "fmt"

// Payment 抽象接口(抽象)
type Payment interface {
    Pay(amount float64) error
}

// CreditCardPayment 具体实现(细节)
type CreditCardPayment struct{}

func (c *CreditCardPayment) Pay(amount float64) error {
    fmt.Printf("Paid %.2f via Credit Card\n", amount)
    return nil
}

// OrderService 高层模块(依赖抽象,不依赖具体实现)
type OrderService struct {
    payment Payment // 依赖Payment接口,而非具体类型
}

func (o *OrderService) ProcessOrder(amount float64) error {
    return o.payment.Pay(amount) // 高层模块通过抽象调用低层功能
}

func main() {
    // 注入具体实现(细节依赖抽象)
    creditCard := &CreditCardPayment{}
    orderService := &OrderService{payment: creditCard}
    orderService.ProcessOrder(300.00)

    // 若需替换为PayPal,只需新增PayPalPayment实现Payment接口,无需修改OrderService
}

迪米特法则(Law of Demeter, LoD)

一个类只与他的直接朋友通信,减少对其他朋友不必要的了解,比如访问其他朋友的成员变量

比如当A通过B访问C的成员变量时,B是A的直接朋友,那么A就不能直接访问到C的成员变量

对于下面代码,Customer只与Order(直接朋友)交互,通过Order的GetTotalPrice获取总价,而非直接访问Product的Price

 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
package main

import "fmt"

// Product 商品(非Customer的直接朋友)
type Product struct {
	Name  string
	Price float64 // 内部细节
}

// Order 订单(Customer的直接朋友)
type Order struct {
	products []Product
}

// GetTotalPrice 订单封装商品价格计算,对外提供结果
func (o *Order) GetTotalPrice() float64 {
	total := 0.0
	for _, p := range o.products {
		total += p.Price // Order可访问Product(Order的直接朋友)
	}
	return total
}

// Customer 客户(只与Order通信,不直接访问Product)
type Customer struct{}

// Checkout 结账:通过Order获取总价,不直接操作Product
func (c *Customer) Checkout(order *Order) {
	total := order.GetTotalPrice() // 只与直接朋友Order通信
	fmt.Printf("Total to pay: %.2f\n", total)
}

func main() {
	products := []Product{
		{Name: "Book", Price: 50.0},
		{Name: "Pen", Price: 5.0},
	}
	order := &Order{products: products}
	customer := &Customer{}
	customer.Checkout(order) // 输出:Total to pay: 55.00
}