坚实的原则:从面向对象编程开始

2025-05-24

坚实的原则:从面向对象编程开始

大家好,今天我想聊聊一些关于固体的东西。
固体?当然不是!没那么固体。
等等……啊,也许就是那么固体!

今天我去面试了,被问到关于“ Solid 原则”的问题。我的反应是“嗯……不太清楚”,意思是“我不知道你在说什么”。
走出面试官的房间时,我突然意识到自己确实需要研究一下 Solid 原则,才能在面向对象编程的道路上迈出正确的一步。

所以今天,我想用一些基本定义和简单的例子来介绍这个主题,以帮助我们全面理解这个概念。

1)是什么以及为什么:坚实的原则

替代文本

SOLID 代表...

SOLID 是罗伯特·C·马丁 (Robert C. Martin) 提出的前五个面向对象设计 (OOD)**原则**的首字母缩写词。

替代文本

它实际上只是我们在编码、改进和构建代码时需要牢记的一组原则。

SOLID 确实如此......

读完之后,我完全不明白它到底是什么意思。就我理解而言,这些原则旨在让开发人员能够更轻松、更清晰地开发软件,使其在规模越来越大、越来越复杂的情况下也易于维护和修改。因此,我们可以真正理解这个概念,它是一种面向对象编程的方法,即通过精简和重构的方式,减少不必要的部分,并组织代码。

2)深入研究:坚实的原则

第一条原则:单一职责原则

“一个类应该有且仅有一个改变的理由,这意味着一个类应该只有一项工作。”

这听起来很简单,意思是一个类应该只执行单一任务,而不是同一个类中的多个任务。
让我们看下面的例子。

class Book {
  protected $Author;

  public getAuthor($Author) {
    return $this->Author;
  }

  public function formatJson() {
    return json_encode($this->getAuthor());
  }
}
Enter fullscreen mode Exit fullscreen mode

你觉得怎么样?你觉得你掌握了吗?
我们可以清楚地看到,名为“Book”的类执行了多项操作,包括获取书籍作者信息,并将输出编码为 Json 格式。但是,如果我们想要输出 Json 以外的其他格式怎么办?在这种情况下,我们可能需要多写几行代码来添加更多方法或修改现有代码。目前看来,这似乎是一项小工作,因为我们现在的示例非常简单,但想象一下,如果有数百甚至数千个类,最好通过实施这些原则来提前避免混淆。

所以我们可以采取如下方法

class Book {
  protected $Author;

  public getAuthor($Author){
    return $this->Author;
  }
}

class JsonBookForm {
    public function format(Book $Author) {
        return json_encode($Author->getAuthor());
    }
}
Enter fullscreen mode Exit fullscreen mode

通过这样做,如果我们想要获得不同类型的输出,那么我们不需要直接在 Book 类中进行更改。

第二原则:开放封闭原则

“对象或实体应该对扩展开放,但对修改关闭。”

我知道这有点无聊,但这实际上意味着应该扩展类来改变功能,而不是修改类。当他们想要添加更多选项时,应该有一种扩展其功能的方式,而不是直接更改现有代码。

有一个常见的例子,如下所示。

class Triangle{
  public $width;
  public $height;
}

class Board {
  public $triangles= [];
  public function calculateArea() {
    $area = 0;
    foreach ($this->triangles as $triangle) {
      $area += $triangle->width * $triangle->height* 1/2;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

我们有一个 Triangle 类,它包含三角形的数据,即宽度和高度;还有一个 Board 类,它用作 Triangle 对象的数组。
这段代码目前看起来完全没问题,但当你考虑形状和面积计算函数时,也许以后你会想把这个函数用于其他形状,而不仅仅是三角形,以提高效率。目前,这几行代码无法做到这一点,因为它限制了 Board 类只能与 Triangle 类配合使用。

我们可以尝试的方法是

interface Shape {
   public function area();
}

class Triangle implements Shape {
  public function area() {
    return $this->width * $this->height *1/2;
  }
}
class Circle implements Shape {
  public function area() {
    return $this->radius * $this->radius * pi();
  }
}
Enter fullscreen mode Exit fullscreen mode
class Board {
  public $shapes;

  public function calculateArea() {
    $area = 0;
    foreach ($this->shapes as $shape) {
      $area+= $shape->area();
    }
    return $area;
  }
}
Enter fullscreen mode Exit fullscreen mode

这样,我们就不需要在类名 Board 中指定类的名称,因此每当我们想要添加更多属性(例如 Rectangle)时,我们都可以轻松添加一个名为自己的类并实现 Shape 接口,以便我们可以在 Board 类中使用 area()。

第三原则:里氏替代原则

“设 q(x) 是关于类型 T 的 x 对象的可证明属性。那么 q(y) 应该对于类型 S 的对象 y 可证明,其中 S 是 T 的子类型。”

好吧,我知道这听起来确实像我高中数学老师说的。我不太明白。
不过看看这张图片, 是不是感觉很熟悉?我们可能在学校见过这张图片,而且我相信你的数学老师也一定提到过。这张图在我们的现实世界中可以隐含的东西与前面的例子有关。形状。
替代文本

说到形状,有很多种,对吧?有圆形、三角形、长方形和正方形……等等,我们都知道长方形和正方形很像但不完全一样,对吧?
长方形包含正方形,因为长方形需要满足更多条件才能成为正方形。所以它会是这样的。

替代文本
哎呀,我把名字写反了!!!
我们可以在这里做的通用代码是

class Rectangle {
  public function setW($w) { 
      $this->width = $w;
  }

  public function setH($h) {
      $this->height = $h;
  }

  public function Area() {
      return $this->height * $this->width;
  }
}
Enter fullscreen mode Exit fullscreen mode
class Square extends Rectangle {
  public function set($w) {
    $this->width = $w;
    $this->height = $w;
  }

  public function setH=($h) {
    $this->height = $h;
    $this->width = $h;
  }
}
Enter fullscreen mode Exit fullscreen mode

但是您是否意识到有多行看起来完全一样?并且考虑到在本教程中,我们正在尽最大努力使代码可维护且高效,因此应该避免这种方式,而应尝试这种方式。

interface Setshape {
  public function setH($h);

  public function setW($w);

  public function Area();
}

class Rectangle implements Setshape ;

class Square implements Setshape ;
Enter fullscreen mode Exit fullscreen mode

通过这种方式,我们不必一遍又一遍地编写相同的代码,而只需简单地实现类的接口即可。

第四个原则:接口隔离原则

“客户端不应该被迫实现它不使用的接口,也不应该被迫依赖它不使用的方法。”

我们快到了,第四个原则意味着不应该强迫类实现它们不使用的接口。例如,假设有一些类看起来像

interface Employee {

  public function generatereport()

  public function clockin()

  public function clockout()

  public function customerservice()

  public function getPaid()

}
Enter fullscreen mode Exit fullscreen mode

假设有一位值班经理,他负责上述大部分功能,但不包括打卡上下班,因为他可能拿的是工资而不是按小时计酬,所以他永远不会使用这两个功能。为了再次遵循这个原则,我们应该这样做。

interface Generalemployee{
 public function clockin()
 public function clockout()
}

interface Employee{
 public function customerservice()
 public function getPaid()
}

interface management{
  public function generatereport()
}

class Staff implements Generalemployee, Employee{
}

class Manager implements Employee, management{
}
Enter fullscreen mode Exit fullscreen mode

最后一个原则:依赖倒置原则

“实体必须依赖于抽象,而不是具体。它指出高级模块不能依赖于低级模块,但它们应该依赖于抽象。”

我将直接讨论这个例子

class Getuserlists {
    private $dbConn;

    public function __construct(MySQLConn, $dbConn) {
        $this->dbConn= $dbConn;
    }
}
Enter fullscreen mode Exit fullscreen mode

这是一个常见的错误,因为根据层次结构,Getuserlists 类是较高模块,而 MySQLConn 是较低模块,但是,您可以轻松注意到该类完全依赖于 MySQLConn,如果我们想使用不仅仅是 MySQL 的其他数据库,那么以后就会造成混淆。

因此,解决方案是......

interface DbConnectionInterface {
    public function connect();
} 

class MySqlConn implements DbConnectionInterface {
    public function connect() {}
}

class Getuserlists {
    private $dbConn;
    public function __construct(DbConnectionInterface $dbConn) {
        $this->dbConn= $dbConn;
    }
}
Enter fullscreen mode Exit fullscreen mode

根据上面的小片段,您现在可以看到高级模块和低级模块都依赖于抽象。

恭喜!!我们向面向对象编程的世界迈进了一步!!

替代文本

结论

SOLID 原则一开始看起来非常令人困惑,为了使我能够轻松使用而不知不觉中,我仍然会遇到更多的困难,但有一些我们可以遵循的指导方针总是好的,不是吗?

文章来源:https://dev.to/ham8821/solid-principles-to-start-with-object-orient-programming-1e49
PREV
6 个用于生成 PDF 的 JS 库的全面比较
NEXT
如何(负责任地)使用 Google 表格作为数据库 TL;DR(30 秒)限制 工作原理 端点