文章

详解设计模式之六大原则(补)

设计模式的六大原则是面向对象编程的核心思想,它们指导我们如何编写灵活、可维护和可扩展的代码。


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

定义:

一个类应该只有一个引起它变化的原因,或者说一个类只负责一项职责。

核心思想:

将功能分解到最小粒度,每个模块或类专注于完成某一个特定的任务。

示例:

PHP 示例:

// 不符合 SRP 的设计

class Report {

    public function generateReport() {

        echo "Generating report...\n";

    }

    public function saveToFile($filename) {

        echo "Saving report to $filename...\n";

    }

}

// 符合 SRP 的设计

class ReportGenerator {

    public function generateReport() {

        echo "Generating report...\n";

    }

}

class ReportSaver {

    public function saveToFile($filename) {

        echo "Saving report to $filename...\n";

    }

}

Go 示例:

// 不符合 SRP 的设计

type Report struct{}

func (r *Report) GenerateReport() {

    fmt.Println("Generating report...")

}

func (r *Report) SaveToFile(filename string) {

    fmt.Printf("Saving report to %s...\n", filename)

}

// 符合 SRP 的设计

type ReportGenerator struct{}

func (rg *ReportGenerator) GenerateReport() {

    fmt.Println("Generating report...")

}

type ReportSaver struct{}

func (rs *ReportSaver) SaveToFile(filename string) {

    fmt.Printf("Saving report to %s...\n", filename)

}

2. 开闭原则(Open/Closed Principle, OCP)

定义:

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

核心思想:

通过抽象化和接口实现,允许在不修改现有代码的情况下扩展功能。

示例:

PHP 示例:

abstract class Shape {

    abstract public function draw();

}

class Circle extends Shape {

    public function draw() {

        echo "Drawing a circle\n";

    }

}

class Rectangle extends Shape {

    public function draw() {

        echo "Drawing a rectangle\n";

    }

}

// 扩展新功能时无需修改现有代码

class Triangle extends Shape {

    public function draw() {

        echo "Drawing a triangle\n";

    }

}

Go 示例:

type Shape interface {

    Draw()

}

type Circle struct{}

func (c *Circle) Draw() {

    fmt.Println("Drawing a circle")

}

type Rectangle struct{}

func (r *Rectangle) Draw() {

    fmt.Println("Drawing a rectangle")

}

// 扩展新功能时无需修改现有代码

type Triangle struct{}

func (t *Triangle) Draw() {

    fmt.Println("Drawing a triangle")

}

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

定义:

子类必须能够替换其父类,并且程序的行为不会因此发生异常或错误。

核心思想:

继承关系应该是“is-a”的语义,子类不仅要继承父类的接口,还要保证行为的一致性。

示例:

PHP 示例:

abstract class Bird {

    abstract public function move();

}

class Sparrow extends Bird {

    public function move() {

        echo "Flying\n";

    }

}

class Ostrich extends Bird {

    public function move() {

        echo "Running\n";

    }

}

Go 示例:

type Bird interface {

    Move()

}

type Sparrow struct{}

func (s *Sparrow) Move() {

    fmt.Println("Flying")

}

type Ostrich struct{}

func (o *Ostrich) Move() {

    fmt.Println("Running")

}

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

定义:

客户端不应该依赖它不需要的接口。换句话说,接口应该尽量细化,避免“胖接口”。

核心思想:

将大而全的接口拆分为多个小而专的接口。

示例:

PHP 示例:

interface Workable {

    public function work();

}

interface Eatable {

    public function eat();

}

class HumanWorker implements Workable, Eatable {

    public function work() {

        echo "Working\n";

    }

    public function eat() {

        echo "Eating\n";

    }

}

class RobotWorker implements Workable {

    public function work() {

        echo "Working\n";

    }

}

Go 示例:

type Workable interface {

    Work()

}

type Eatable interface {

    Eat()

}

type HumanWorker struct{}

func (hw *HumanWorker) Work() {

    fmt.Println("Working")

}

func (hw *HumanWorker) Eat() {

    fmt.Println("Eating")

}

type RobotWorker struct{}

func (rw *RobotWorker) Work() {

    fmt.Println("Working")

}

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

定义:

高层模块不应该依赖低层模块,二者都应该依赖于抽象;抽象不应该依赖细节,细节应该依赖抽象。

核心思想:

通过引入抽象层,解耦高层模块和低层模块。

示例:

PHP 示例:

interface Car {

    public function drive();

}

class BMW implements Car {

    public function drive() {

        echo "Driving BMW\n";

    }

}

class Driver {

    private $car;

    public function __construct(Car $car) {

        $this->car = $car;

    }

    public function driveCar() {

        $this->car->drive();

    }

}

Go 示例:

type Car interface {

    Drive()

}

type BMW struct{}

func (b *BMW) Drive() {

    fmt.Println("Driving BMW")

}

type Driver struct {

    car Car

}

func NewDriver(car Car) *Driver {

    return &Driver{car: car}

}

func (d *Driver) DriveCar() {

    d.car.Drive()

}

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

定义:

一个对象应该对其他对象有最少的了解,或者说一个类应该只与直接的朋友通信。

核心思想:

减少类之间的耦合,避免一个类直接操作另一个类的内部细节。

示例:

PHP 示例:

class Address {

    public function getCity() {

        return "New York";

    }

}

class Student {

    private $address;

    public function __construct(Address $address) {

        $this->address = $address;

    }

    public function getCity() {

        return $this->address->getCity();

    }

}

class School {

    public function printStudentCity(Student $student) {

        echo $student->getCity() . "\n";

    }

}

Go 示例:

type Address struct{}

func (a *Address) GetCity() string {

    return "New York"

}

type Student struct {

    address *Address

}

func NewStudent(address Address) Student {

    return &Student{address: address}

}

func (s *Student) GetCity() string {

    return s.address.GetCity()

}

type School struct{}

func (sch School) PrintStudentCity(student Student) {

    fmt.Println(student.GetCity())

}

总结

设计模式的六大原则是构建高质量软件的基础。通过遵循这些原则,我们可以编写出更加灵活、可维护和可扩展的代码。无论是 PHP 还是 Go,这些原则都同样适用。在实际开发中,结合具体语言的特点,合理运用这些原则,可以帮助我们设计出更加优雅的系统架构。

License:  CC BY 4.0