学会这样写代码,一看就是资深工程师,代码简洁之道PHP版本

news2024/11/27 10:44:11

文章目录

  • 一、前言
  • 二、规范
    • 2.1 整体结构规范
      • 2.1.1 类的括号前括号单独一行
      • 2.1.2 方法的前括号单独一行
      • 2.1.3 方法内部语句前括号不换行
    • 2.2 变量与常量
      • 2.2.1 变量的命名尽量要有语义
      • 2.2.2 同一个实体要用相同的变量名
      • 2.2.3 尽量使用有语义的常量表述
      • 2.2.4 使用自解释型变量
      • 2.2.5 避免使用无意义的变量名
      • 2.2.6 进行适当的变量约束
    • 2.3 基础代码结构
      • 2.3.1 避免深层次嵌套
      • 2.3.2 使用简洁的上下文
      • 2.3.3 使用严格恒等表达式
      • 2.3.5 尽可能的封装条件语句
      • 2.3.6 尽可能用正义条件判断
      • 2.3.7 避免条件判断
      • 2.3.8 避免类型检查 (part 1)
      • 2.3.9 避免类型检查 (part 2)
      • 2.3.10 移除僵尸代码
    • 2.4 方法和函数
      • 2.4.1 函数参数(最好少于2个)
      • 2.4.2 函数应该只做一件事
      • 2.4.3 函数名应该是有意义的动词(或表明具体做了什么事)
      • 2.4.4 函数里应当只有一层抽象abstraction
      • 2.4.5 不要用flag作为函数的参数
      • 2.4.6 不要写全局函数
    • 2.5 对象和数据结构
      • 2.5.1 使用 getters 和 setters
      • 2.5.2 对象属性多使用private/protected限定
    • 2.6 类和设计模式
      • 2.6.1 组合优于继承
      • 2.6.2 推荐使用 final 类
    • 2.7 SOLID设计原则
      • 2.7.1 SRP
      • 2.7.2 OCP
      • 2.7.3 LSP
      • 2.7.4 ISP
      • 2.7.5 DIP
  • 总结


一、前言

本文已收录于PHP全栈系列专栏:PHP快速入门与实战

写代码是一种职业,每个人对写代码的理解不一样,代码水平也不一样,编写简洁的可读性代码不仅让自己很有成就感,也能提升别人对你的认识,同时也对整个项目的质量有着非常可靠的保证。不过写好代码并不是一件十分容易的事情,本文就从多年代码写作经验以及业内普遍认识出发,介绍一些常见的代码简洁之道。供大家一起交流学习。
在这里插入图片描述

二、规范

2.1 整体结构规范

2.1.1 类的括号前括号单独一行

class LumenServiceProvider extends AbstractServiceProvider
{
    // ...
}

2.1.2 方法的前括号单独一行

public function boot()
{
    // ...
}

2.1.3 方法内部语句前括号不换行

protected function setGlobalAddress($address)
{
    if (is_array($address) && isset($address['address'])) {
        // ...
    }
}

2.2 变量与常量

2.2.1 变量的命名尽量要有语义

$ymd = date('y-m-d');

$currentDate = date('y-m-d');

2.2.2 同一个实体要用相同的变量名

坏:

getOrderInfo();
getOrderData();
getOrderRecord();

好:

getOrder();

2.2.3 尽量使用有语义的常量表述

坏:

if ($order->paid & 2) {
    // ...
}

好:

class Order
{
    const ORDER_SHIPPED = 2;
}

if ($order->paid & Order::ORDER_SHIPPED) {
    // do edit ...
}

2.2.4 使用自解释型变量

坏:

$about = 'My introduction, XiaoMin 18699998888';
$nameAndPhoneRegex = '/^[^,]+,\s*(.+?)\s*(\d{11})$/';
preg_match($nameAndPhoneRegex, $about, $matches);

var_dump($matches[1], $matches[2]);

不错:
好一些,但强依赖于正则表达式的熟悉程度

$about = 'My introduction, XiaoMin 18699998888';
$nameAndPhoneRegex = '/^[^,]+,\s*(.+?)\s*(\d{11})$/';
preg_match($nameAndPhoneRegex, $about, $matches);

[, $city, $zipCode] = $matches;
var_dump($city, $zipCode);

好:
使用带名字的子规则,不用懂正则也能看的懂

$about = 'My introduction, XiaoMin 18699998888';
$nameAndPhoneRegex = '/^[^,]+,\s*(?<name>.+?)\s*(?<phone>\d{11})$/';
preg_match($nameAndPhoneRegex, $about, $matches);

var_dump($matches['name'], $matches['phone']);

2.2.5 避免使用无意义的变量名

坏:

$l = ['Apple', 'Orange', 'Grape'];
// 下面的li和$i代表的什么没有意义
for ($i = 0; $i < count($l); $i++) {
    $li = $l[$i];
    ...
}

好:

$fruits = ['Austin', 'New York', 'San Francisco'];
 
foreach ($fruits as $fruit) {
    eat($fruit);
}

2.2.6 进行适当的变量约束

坏:


function createTemplate($templateName = 'email'): void
{
    // ...
}

好:

function createTemplate(string $templateName = 'email'): void
{
    // ...
}

2.3 基础代码结构

2.3.1 避免深层次嵌套

太多的if else语句通常会导致你的代码难以阅读,直白优于隐晦

糟糕:

function isWeekend($day): bool
{
    if ($day) {
        if (is_string($day)) {
            $day = strtolower($day);
            if ($day === 'saturday') {
                return true;
            } elseif ($day === 'sunday') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    } else {
        return false;
    }
}

好:

function isWeekend(string $day): bool
{
    if (empty($day)) {
        return false;
    }
 
    $weekendDays = [
        'saturday', 'sunday'
    ];
 
    return in_array(strtolower($day), $weekendDays, true);
}

2.3.2 使用简洁的上下文

如果从你的类名、对象名已经可以得知一些信息,就别再在变量名里重复。
坏:

class Goods
{
    public $goodsSize;
    public $goodsColor;
    public $goodsSku;
 
    //...
}

好:

class Goods
{
    public $size;
    public $color;
    public $sku;
 
    //...
}

2.3.3 使用严格恒等表达式

坏:

$a = '42';
$b = 42;
 
if( $a != $b ) {
   //这里始终执行不到
}

好:

$a = '42';
$b = 42;
 
if ($a !== $b) {
    // 
}

2.3.5 尽可能的封装条件语句

坏:

if ($goods->state === 'available') {
    // ...
}

好:

function isAvailable() 
{
    return $this->state === 'available';
}
if ($goods->isAvailable()) {
    // ...
}

2.3.6 尽可能用正义条件判断

坏:
避免用反义条件判断

function isNotStudent(Student $student): bool
{
    // ...
}

if (isNotStudent($student))
{
    // ...
}

好:

function isStudent(Student $student): bool
{
    // ...
}

if (!isStudent($node)) {
    // ...
}

2.3.7 避免条件判断

这看起来像一个不可能任务。当人们第一次听到这句话是都会这么说。“没有if语句我还能做啥?” 答案是你可以使用多态来实现多种场景 的相同任务。第二个问题很常见, “这么做可以,但为什么我要这么做?”
答案是前面我们学过的一个Clean Code原则:一个函数应当只做一件事。
当你有很多含有if语句的类和函数时,你的函数做了不止一件事。
记住,只做一件事。
坏:

class Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        switch ($this->type) {
            case '777':
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case 'Air Force One':
                return $this->getMaxAltitude();
            case 'Cessna':
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

好:

interface Airplane
{
    // ...

    public function getCruisingAltitude(): int;
}

class Boeing777 implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}

class AirForceOne implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude();
    }
}

class Cessna implements Airplane
{
    // ...

    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

2.3.8 避免类型检查 (part 1)

PHP是弱类型的,这意味着你的函数可以接收任何类型的参数。
有时候你为这自由所痛苦并且在你的函数渐渐尝试类型检查。
有很多方法去避免这么做。第一种是统一API。

坏:

function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}

好:

function travelToTexas(Traveler $vehicle): void
{
    $vehicle->travelTo(new Location('texas'));
}

2.3.9 避免类型检查 (part 2)

如果你正使用基本原始值比如字符串、整形和数组,要求版本是PHP 7+,不用多态,需要类型检测,
那你应当考虑类型声明或者严格模式。
提供了基于标准PHP语法的静态类型。 手动检查类型的问题是做好了需要好多的废话,好像为了安全就可以不顾损失可读性。
保持你的PHP 整洁,写好测试,做好代码回顾。做不到就用PHP严格类型声明和严格模式来确保安全。

坏:

function combineArray($arr1, $arr2): array
{
    if (!is_array($arr1) || !is_array($arr2)) {
        throw new \Exception('Must be of type Array');
    }

    return $val1 + $val2;
}

好:

function combineArray(array $val1, array $val2): array
{
    return $val1 + $val2;
}

2.3.10 移除僵尸代码

僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来没被调用过,就删掉!
因为还在代码版本库里,因此很安全。
坏:

function oldRequestLog(string $url): void
{
    // ...
}

function newRequestLog(string $url): void
{
    // ...
}

$request = newRequestLog($requestUrl);

好:

function requestLog(string $url): void
{
    // ...
}

$request = requestLog($requestUrl);

2.4 方法和函数

2.4.1 函数参数(最好少于2个)

限制函数参数个数极其重要,这样测试你的函数容易点。有超过3个可选参数参数导致一个爆炸式组合增长,你会有成吨独立参数情形要测试。
无参数是理想情况。1个或2个都可以,最好避免3个。再多就需要加固了。通常如果你的函数有超过两个参数,说明他要处理的事太多了。 如果必须要传入很多数据,建议封装一个高级别对象作为参数。
坏:

function createActivity(string $title, string $body, string $date, bool $published): void
{
// ...
}

好:

class Activity
{
    public $title;
    public $body;
    public $date;
    public $published = false;
}

$config = new Activity();
$config->title = '中秋活动';
$config->body = '快来领月饼了';
$config->date = '2023-06-28';
$config->published = true;

function createActivity(Activity $activity): void
{
// ...
}

2.4.2 函数应该只做一件事

这是迄今为止软件工程里最重要的一个规则。当一个函数做超过一件事的时候,他们就难于实现、测试和理解。当你把一个函数拆分到只剩一个功能时,他们就容易被重构,然后你的代码读起来就更清晰。如果你光遵循这条规则,你就领先于大多数开发者了。
坏:

function emailClients(array $clients): void
{
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);
        if ($clientRecord->isActive()) {
            email($client);
        }
    }
}

好:

function emailClients(array $clients): void
{
    array_walk(activeClients($clients), 'email');
}

function activeClients(array $clients): array
{
    return array_filter($clients, 'isClientActive');
}

function isClientActive(int $client): bool
{
    return $db->find($client)->isActive();
}

2.4.3 函数名应该是有意义的动词(或表明具体做了什么事)

坏:

class Message
{
    //...
    public function start(): void
    {
        //...
    }
}
$message = new Message();
// start干了什么事情不明确
$message->start();

好:

class Message
{
    //...
    public function push(): void
    {
        //...
    }
}
$message = new Email(...);
// 简单明了, 消息推送
$message->push();

2.4.4 函数里应当只有一层抽象abstraction

当你抽象层次过多时时,函数处理的事情太多了。需要拆分功能来提高可重用性和易用性,以便简化测试。
(译者注:这里从示例代码看应该是指嵌套过多)

坏:

function parseBetterJSAlternative(string $code): void
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }

    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }

    foreach ($ast as $node) {
        // parse...
    }
}

坏:

我们把一些方法从循环中提取出来,但是parseBetterJSAlternative()方法还是很复杂,而且不利于测试。

function tokenize(string $code): array
{
    $regexes = [
        // ...
    ];

    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }

    return $tokens;
}

function lexer(array $tokens): array
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }

    return $ast;
}

function parseBetterJSAlternative(string $code): void
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // 解析逻辑...
    }
}

好:
最好的解决方案是把 parseBetterJSAlternative()方法的依赖移除。

class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];

        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }

        return $ast;
    }
}

class BetterJSAlternative
{
    private $tokenizer;
    private $lexer;

    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }

    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // 解析逻辑...
        }
    }
}

这样我们可以对依赖做mock,并测试BetterJSAlternative::parse()运行是否符合预期。

2.4.5 不要用flag作为函数的参数

flag就是在告诉大家,这个方法里处理很多事。前面刚说过,一个函数应当只做一件事。 把不同flag的代码拆分到多个函数里。
坏:

function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/'.$name);
    } else {
        touch($name);
    }
}

好:

function createFile(string $name): void
{
    touch($name);
}

function createTempFile(string $name): void
{
    touch('./temp/'.$name);
}

2.4.6 不要写全局函数

在大多数语言中污染全局变量是一个坏的实践,因为你可能和其他类库冲突
并且调用你api的人直到他们捕获异常才知道踩坑了。让我们思考一种场景:
如果你想配置一个数组,你可能会写一个全局函数config(),但是他可能
和试着做同样事的其他类库冲突。

坏:

function config(): array
{
    return  [
        'foo' => 'bar',
    ]
}

好:

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get(string $key): ?string
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}

加载配置并创建 Configuration 类的实例

$configuration = new Configuration([
    'foo' => 'bar',
]);

现在你必须在程序中用 Configuration 的实例了

2.5 对象和数据结构

2.5.1 使用 getters 和 setters

在PHP中你可以对方法使用public, protected, private 来控制对象属性的变更。

  • 当你想对对象属性做获取之外的操作时,你不需要在代码中去寻找并修改每一个该属性访问方法
  • 当有set对应的属性方法时,易于增加参数的验证
  • 封装内部的表示
  • 使用set和get时,易于增加日志和错误控制
  • 继承当前类时,可以复写默认的方法功能
  • 当对象属性是从远端服务器获取时,get*,set*易于使用延迟加载

此外,这样的方式也符合OOP开发中的开闭原则

坏:

class BankAccount
{
    public $balance = 1000;
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->balance -= 100;

好:

class BankAccount
{
    private $balance;

    public function __construct(int $balance = 1000)
    {
      $this->balance = $balance;
    }

    public function withdraw(int $amount): void
    {
        if ($amount > $this->balance) {
            throw new \Exception('Amount greater than available balance.');
        }

        $this->balance -= $amount;
    }

    public function deposit(int $amount): void
    {
        $this->balance += $amount;
    }

    public function getBalance(): int
    {
        return $this->balance;
    }
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->withdraw($shoesPrice);

// Get balance
$balance = $bankAccount->getBalance();

2.5.2 对象属性多使用private/protected限定

  • public方法和属性进行修改非常危险,因为外部代码容易依赖他,而你没办法控制。对之修改影响所有这个类的使用者。 public methods and properties are most dangerous for changes, because some outside code may easily rely on them and you can’t control what code relies on them. Modifications in class are dangerous for all users of class.
  • protected的修改跟对public修改差不多危险,因为他们对子类可用,他俩的唯一区别就是可调用的位置不一样,对之修改影响所有集成这个类的地方。 protected modifier are as dangerous as public, because they are available in scope of any child class. This effectively means that difference between public and protected is only in access mechanism, but encapsulation guarantee remains the same. Modifications in class are dangerous for all descendant classes.
  • private的修改保证了这部分代码只会影响当前类private modifier guarantees that code is dangerous to modify only in boundaries of single class (you are safe for modifications and you won’t have Jenga effect).

所以,当你需要控制类里的代码可以被访问时才用public/protected,其他时候都用private

可以读一读这篇 博客文章 ,Fabien Potencier写的.

坏:

class BankAccount
{
    public $money;

    public function __construct(float $money)
    {
        $this->money = $money;
    }
}

$bankAccount = new BankAccount(1000);
echo 'Bank account: '.$bankAccount->money; // Bank account: 1000

好:

class BankAccount
{
    private $money;

    public function __construct(float $money)
    {
        $this->money = $money;
    }

    public function getMoney(): string
    {
        return $this->money;
    }
}

$employee = new BankAccount(1000);
echo 'Bank account: '.$employee->getMoney(); // Bank account: 1000

2.6 类和设计模式

2.6.1 组合优于继承

正如 the Gang of Four 所著的设计模式之前所说,
我们应该尽量优先选择组合而不是继承的方式。使用继承和组合都有很多好处。
这个准则的主要意义在于当你本能的使用继承时,试着思考一下组合是否能更好对你的需求建模。
在一些情况下,是这样的。

接下来你或许会想,“那我应该在什么时候使用继承?”
答案依赖于你的问题,当然下面有一些何时继承比组合更好的说明:

  1. 你的继承表达了“是一个”而不是“有一个”的关系(人类-》动物,用户-》用户详情)
  2. 你可以复用基类的代码(人类可以像动物一样移动)
  3. 你想通过修改基类对所有派生类做全局的修改(当动物移动时,修改她们的能量消耗)

坏:

class Order 
{
    private $orderSn;
    private $email;

    public function __construct(string $orderSn, string $email)
    {
        $this->orderSn = $orderSn;
        $this->email = $email;
    }

    // ...
}


// 不好,因为OrderGoodsData不是Order类型的
class OrderGoodsData extends Order 
{
    private $goodsId;
    private $goodsNumber;
    
    public function __construct(string $orderSn, string $email, string $goodsId, string $goodsNumber)
    {
        parent::__construct($orderSn, $email);

        $this->goodsId = $goodsId;
        $this->goodsNumber = $goodsNumber;
    }

    // ...
}

好:

class OrderGoodsData 
{
    private $goodsId;
    private $goodsNumber;

    public function __construct(string $goodsId, string $goodsNumber)
    {
        $this->goodsId = $goodsId;
        $this->goodsNumber = $goodsNumber;
    }

    // ...
}

class Order 
{
    private $orderSn;
    private $email;
    /** @var $orderGoodsData OrderGoodsData */
    private $orderGoodsData;

    public function __construct(string $orderSn, string $email)
    {
        $this->orderSn = $orderSn;
        $this->email = $email;
    }

    public function setGoodsData(string $goodsId, string $goodsNumber)
    {
        $this->orderGoodsData = new OrderGoodsData($goodsId, $goodsNumber);
    }

    // ...
}

2.6.2 推荐使用 final 类

能用时尽量使用 final 关键字:

  1. 阻止不受控的继承链
  2. 鼓励 组合.
  3. 鼓励 单一职责模式.
  4. 鼓励开发者用你的公开方法而非通过继承类获取受保护方法的访问权限.
  5. 使得在不破坏使用你的类的应用的情况下修改代码成为可能.

The only condition is that your class should implement an interface and no other public methods are defined.

For more informations you can read the blog post on this topic written by Marco Pivetta (Ocramius).

坏:

final class Car
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * @return string The color of the vehicle
     */
    public function getColor() 
    {
        return $this->color;
    }
}

好:

interface Vehicle
{
    /**
     * @return string The color of the vehicle
     */
    public function getColor();
}

final class Car implements Vehicle
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getColor() 
    {
        return $this->color;
    }
}

2.7 SOLID设计原则

SOLID 是面向对象设计中的五个原则,它们的介绍如下。这些原则旨在提高代码的可重用性、可维护性和可扩展性。

  • S: 职责单一原则 (SRP,Single Responsibility Principle)
  • O: 开闭原则 (OCP,Open-Closed Principle)
  • L: 里氏替换原则 (LSP,Open-Closed Principle)
  • I: 接口隔离原则 (ISP,Interface Segregation Principle)
  • D: 依赖反转原则 (DIP,Dependency Inversion Principle)

2.7.1 SRP

Single Responsibility Principle (SRP) 单一责任原则指一个类只应该有一个引起它变化的原因,或者说一个类只应该负责一项职责。当一个类负责多个不同的职责时,修改其中一个职责可能会影响到其他职责,导致代码变得脆弱且难以维护。 例如,在PHP中,我们可能有一个名为User的类,它负责处理用户登录、注册和密码重置等操作。但是这样会导致User类职责过于庞大,此时可以考虑将其拆分成多个类,比如Login、Register和PasswordReset类。

2.7.2 OCP

Open-Closed Principle (OCP) 开闭原则指一个类应该对扩展(新增功能)开放,而对修改(现有功能的修改)关闭。当需要添加新功能时,我们应该使用扩展而非修改已有的代码。 例如,在PHP中,我们可能有一个名为Customer的类,它负责计算顾客的总价并应用折扣。如果我们需要添加另一个折扣类型,可以通过创建一个新的Discount类并让Customer使用该类来实现扩展,而不是修改Customer类的代码。

2.7.3 LSP

Liskov Substitution Principle (LSP) 里氏替换原则指如果一个父类可以使用它的子类来替换,那么这些子类应该能够无缝地替换父类而不会破坏程序的正确性。 例如,在PHP中,我们可能有一个名为Animal的父类和一个名为Cat的子类。如果我们有一个方法接受父类Animal作为参数,那么我们也应该能够使用子类Cat来代替Animal,而代码不会出现意外错误。

2.7.4 ISP

Interface Segregation Principle (ISP) 接口隔离原则指客户端不应该依赖于他们不需要的接口。当一个接口包含太多的方法时,客户端可能会依赖于不需使用的方法,从而导致代码冗余和臃肿。 例如,在PHP中,我们可能有一个名为Payment的接口,它定义了支付的方法。如果我们也将不相关的方法添加到该接口中,比如退款、资金冻结等,那么客户端可能会依赖于这些不需使用的方法,从而导致代码冗余。此时,我们可以使用多个小接口来替代一个大接口。

2.7.5 DIP

Dependency Inversion Principle (DIP) 依赖倒置原则指高层次的模块不应该依赖于低层次的模块,而两者都应该依赖于抽象。抽象不应该依赖于具体实现,而具体实现应该依赖于抽象。 例如,在PHP中,我们可能有一个名为UserService的类,它依赖于UserRepository类来操作数据库。如果我们直接在UserService中创建UserRepository对象,那么UserService将直接依赖于具体实现。为了遵循DIP原则,我们可以使用依赖注入(Dependency Injection)来解耦UserService和UserRepository。

总结

以上就是今天要讲的内容,学会这样写代码,一看就是资深工程师,代码简洁之道PHP版本。后续更多内容将收录在专栏PHP快速入门与实战中,感谢大家支持与关注,更多有深度的内容将会不断更新。

本文部分内容参考自https://github.com/php-cpm/clean-code-php

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/615675.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

六面钻调试第八步Cam参数调试(一)之压轮压板 ,机械规格调试

Cam参数调试 &#xff08;1&#xff09;.压轮压板参数调试 注&#xff1a;压板的规格测量设置 压板的位置相对基准钻的位置设置 &#xff08;2&#xff09;机械规格调试 1.定位气缸的类型 2.活动式定位气缸的Y向宽度 4.定位杆与夹钳Y边缘的最小间隙 5.活动式定位气缸相对基准…

CVPR视频理解论文

Learning Transferable Spatiotemporal Representations from Natural Script Knowledge 时空TransformerCLIP的对比学习思路

if/while/for/语句/分支/路径覆盖的控制流程图+数据流分析(DU)

if/while/for/语句覆盖/分支覆盖/路径覆盖的控制流程图数据流分析(DU) 语句的线性序列Linear Sequences of Statements “If” Constructs “While” Constructs “For” Constructs 语句覆盖率Statement Coverage 测试套件应执行 CFG 的所有节点 也被称为&#xff1a;…

为什么客服系统必备知识库?提高客服效率的秘密武器!

对任何想要成功的企业而言&#xff0c;客户服务是必不可少的。依据提供的客户支持质量&#xff0c;可建立或破坏一个品牌的声誉。为了提供高标准的客户服务&#xff0c;企业必须能够获得可靠的信息&#xff0c;并能够为用户提供快速有效的解决方案。下面&#xff0c;我们就详细…

matlab学习1--基础

文章目录 基本语法保存和加载数组/矩阵矩阵运算 输出多个值绘制向量图 基本语法 和弱语言一样&#xff0c;变量赋值不需要声明类型直接 a 1 2命名规则 以字母开头&#xff0c;并且仅包含字母、数字和下划线 (_) 区分大小写清除命令行窗口 clc保存和加载 保存 保存到xxx.…

python开发构建轻量级卷积神经网络模型实现手写甲骨文识别系统

手写汉字、手写数字、手写字母识别模型都已经做过很多了&#xff0c;但是手写甲骨文识别这个应该都是很少有听说过的吧&#xff0c;今天也是看到这个数据集就想着基于这批手写甲骨文数据集开发构建识别模型&#xff0c;首先来看下效果图&#xff1a; 接下来看下对应使用的数据集…

字典的学习笔记

列表 [] 单身什么是字典 {} 二人世界 python内置的数据结构之一&#xff0c;与列表一样是一个可变序列(可以增删改操作的) 以键值对的方式存储数据&#xff0c;字典是一个无序的序列 -> hash(key) 通过哈希函数来计算存储位置,key一定是不可变的字典的创建 使用花…

读论文-MHFormer

论文&#xff1a;Multi-hypothesis representation learning for transformer-based 3D human pose estimation 摘要 尽管取得了重大进展&#xff0c;但由于深度模糊和自遮挡&#xff0c;从单目视频中估计3D人体姿势仍然是一项具有挑战性的任务。大多数现有的作品都试图通过利用…

驱动程序接口和HAL层区别和联系

驱动程序接口&#xff08;Device Driver Interface&#xff09;和硬件抽象层&#xff08;Hardware Abstraction Layer&#xff0c;HAL&#xff09;是在软件系统中用于处理硬件的两个不同层次的概念。 驱动程序接口&#xff08;Device Driver Interface&#xff09;&#xff1a;…

设计模式(二、三):创建型之工厂方法和抽象工厂模式

设计模式系列文章 设计模式(一)&#xff1a;创建型之单例模式 设计模式(二、三)&#xff1a;创建型之工厂方法和抽象工厂模式 设计模式(四)&#xff1a;创建型之原型模式 设计模式(五)&#xff1a;创建型之建造者模式 设计模式(六)&#xff1a;结构型之代理模式 目录 一、…

校园综合能效平台建设的意义

摘 要&#xff1a;为响应国家绿色校园建设的号召&#xff0c;切实提高高校能源利用水平&#xff0c;促进学校能源资源合理配置&#xff0c;服务学校高质量发展大局&#xff0c;根据教育部印发的《关于开展节能减排学校行动的通知》《关于勤俭节约办教育建设节约型校园的通知》…

Linux内核超级装备eBPF技术详细研究

定义一&#xff08;http://ebpf.io的定义&#xff09; eBPF (which is no longer an acronym for anything) is a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in a privileged context such as the operating system kernel.…

物联网通信之串口服务器,RS485/RS232双串口并行、远程虚拟串口调试

随着现代工业信息技术发展&#xff0c;串口服务器在工业应用中越来越常见&#xff0c;那么什么是串口服务器呢&#xff0c;今天智联物联小编就与大家分享一下物联网通信中的串口服务器。 为帮助大家理解&#xff0c;智联物联小编从串口服务器的接口为大家开始讲解&#xff0c;一…

高分子PEG:mPEG-Maleimide MW:3400,甲氧基-聚乙二醇—马来酰亚胺,常用作聚合物试剂

【产品描述】 陕西新研博美生物科技有限公司供应的​mPEG-Maleimide属于高分子PEG&#xff0c;马来酰亚胺和巯基的偶合是蛋白和多肽偶联中的一个非常有用的反应。mPEG-MAL被用来合成具有确定结构和生物活性的PEG-蛋白质偶合物。mPEG-MAL也常用作聚合物试剂来选择性诱捕含巯基的…

易基因:小檗碱通过介导m6A mRNA甲基化调控斑马鱼肝细胞氧化应激、凋亡和自噬|科研进展

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 中药小檗碱&#xff08;Berberine&#xff0c;BBR&#xff0c;化学式C20H18NO4&#xff09;是从几种药用植物中分离出的一种异喹啉季生物碱&#xff0c;包括小檗&#xff08;Berberis ar…

让你不再好奇怎样同声传译

众所周知&#xff0c;同声传译技术在国际交流和商务领域发挥着重要的作用&#xff0c;它能够帮助人们跨越语言障碍&#xff0c;促进人们之间的有效沟通。那么&#xff0c;你知道如何同声传译吗&#xff1f;接下来我将教你三个方法&#xff0c;帮助你更好的进行同声传译操作。 方…

springboot+vue餐厅点餐系统在线点餐系统(含源码+数据库)

1.系统分析 系统用例图如下所示。 从用户、餐厅等方面进行需求分析如下。 1.用户需求&#xff1a;系统应该提供简单易用的用户界面&#xff0c;用户可以浏览餐厅菜单&#xff0c;选择菜品&#xff0c;下订单。此外&#xff0c;应该允许用户管理个人信息和查看历史订单。 2.餐…

SQL-多表查询-事务

SQL-多表查询-事务 多表查询顾名思义就是从多张表中一次性的查询出我们想要的数据 前期表准备 DROP TABLE IF EXISTS emp; DROP TABLE IF EXISTS dept;# 创建部门表CREATE TABLE dept(did INT PRIMARY KEY AUTO_INCREMENT,dname VARCHAR(20));# 创建员工表CREATE TABLE emp (i…

一文3000字从0到1用Python做安全测试攻击实战(建议收藏)

在本文中&#xff0c;我们将使用Python进行一次安全测试的实战演练&#xff0c;目标是找出并利用应用程序的安全漏洞。请注意&#xff0c;这个演练仅用于教育和研究目的&#xff0c;切勿将这些技术用于非法活动。 注意&#xff1a;未经授权的攻击是违法的。确保你在拥有明确权…

准实时刷新集群中各节点本地缓存的解决方案

目录 背景 Redis发布订阅 MQ广播消息 配置中心Nacos&#xff0c;Zookeeper监听 注册中心获取服务节点ip端口接口调用 本地定时任务兜底 背景 我们在系统开发过程中&#xff0c;为了减少数据库和redis缓存的查询以提升接口性能&#xff0c;有时候会把一些常用的、变动不是…