oop编程方法_什么是php oop方法中的代码气味以获取更清洁的代码

oop编程方法

动机 (Motivation)

Many programmers gain too much confidence about their code very quickly with the argument “This is how I do it.” And they feel good ignoring the risks. My approach is to follow as many objective rules and best practices as possible. Programming has been improving for decades, and there are many great proven concepts. We save ourselves a lot of time learning them before typing our first line of code. Actually, just a few basic OOP rules help us write significantly better (safer) code — Google SOLID, DRY, SoC, Patterns, and Antipatterns.

许多程序员使用“这就是我的做法”这样的论点很快就对其代码变得过于自信。 他们忽略了风险,感觉很好。 我的方法是遵循尽可能多的客观规则和最佳实践。 数十年来,编程一直在不断改进,并且有许多成熟的概念。 在键入第一行代码之前,我们节省了很多时间来学习它们。 实际上,只有一些基本的OOP规则可以帮助我们编写出更好(更安全)的代码-Google SOLID,DRY,SoC,模式和反模式。

A code smell is a term describing suspicious code constructions. These might not be errors or bugs per se but are most likely problematic from the object-oriented programming (OOP) perspective. In this article, we will highlight the most common ones, and show how to improve the code using simple examples.

代码气味是描述可疑代码构造的术语。 这些本身可能不是错误或错误,但是从面向对象编程(OOP)的角度来看很可能是有问题的。 在本文中,我们将重点介绍最常见的代码,并通过简单的示例演示如何改进代码。

1.功能说明中的“ Ands”过多 (1. Too Many Ands in the Functionality Description)

Whenever you describe the purpose of your classes, pay attention to how many times you use ‘and’. As code grows, we naturally tend to put too many functions into a single class or too many functionalities into a single method.

每当您描述类的目的时,请注意使用了“和”的次数 。 随着代码的增长,我们自然倾向于将太多的函数放在一个类中,或者将太多的功能放在一个方法中。

The issue with big classes is that they are hard to understand, difficult for testing, and extremely difficult to extend. Remember that one of the most important principles in OOP is the Open-Closed Principle [1]. Classes should be closed for modification, but open for extension. Any class that does way too much is very difficult for extension while keeping the complex original functionality the same.

大类的问题在于它们很难理解,难以测试并且极难扩展。 请记住,OOP中最重要的原则之一就是开放式封闭式原则[ 1 ]。 应该关闭类以进行修改,但应打开以进行扩展。 任何做得太多的类都很难扩展,同时又要保持复杂的原始功能不变。

(Example)

I am not a fan of the Laravel’s ORM system Eloquent, because the Laravel’s models do way too much. They carry data, and communicate with a database, and map database data into the model attributes, and handles events, etc. Way too many responsibilities of a single class.

我不喜欢Laravel的ORM系统Eloquent,因为Laravel的模型做得太多。 它们携带数据, 与数据库进行通信, 并将数据库数据映射到模型属性中, 处理事件等。处理单个类的职责过多。

A better approach might be to split the responsibilities into the following layers:

更好的方法可能是将职责分为以下几层:

  • Controller — validates incoming data

    控制器 -验证输入数据

  • Service — contains business logic

    服务 -包含业务逻辑

  • Model — carries data

    模型 -携带数据

  • Repository — prepares data for the database

    储存库 -为数据库准备数据

  • DBMapper — maps models into database rows using a ModelMap which knows how to properly map the model attributes to the database columns

    DBMapper —使用ModelMap将模型映射到数据库行中,该知道如何正确地将模型属性映射到数据库列

(We use this approach in our LayerFrame module for Laravel, described here.)

(我们使用这种方法,我们的Laravel LayerFrame模块,描述在这里 。)

Incoming request models should not be reused for repository or DB Mapper. They should be considered the sole property of a controller. Whatever further processing has to happen by mapping these models into business models and vice versa.

传入的请求模型不应再用于存储库或DB Mapper。 应该将它们视为控制器的唯一属性。 通过将这些模型映射到业务模型中,无论采取什么进一步处理,反之亦然。

2.班级中的“新”关键字 (2. The ‘New’ Keyword in Classes)

Whenever you see new inside a class, it is probably a code smell. Creating a class inside another class creates a very tight coupling between those two. It is hard to test the classes separately because you cannot change the behaviour of the internal class. Plus, if you ever need to extend the functionality, you will have a hard time implementing it safely.

每当您在类中看到内容时,都有可能是代码气味。 在另一个类中创建一个类会在这两个类之间建立非常紧密的联系。 很难单独测试这些类,因为您不能更改内部类的行为。 另外,如果您需要扩展功能,则很难安全地实现它。

(Example)

class WebSearch
{
protected $httpClient;
public function __constructor()
{
$this->httpClient = new HttpClient();
}
}

This is a typical example — how a tight coupling is created between two or more classes. The proper way of using an object inside another object is to pass it into the constructor.

这是一个典型示例-如何在两个或多个类之间创建紧密耦合。 在另一个对象中使用对象的正确方法是将其传递给构造函数。

class WebSearch
{
protected $httpClient; public function __constructor(HttpClient $httpClient)
{
$this->httpClient = $httpClient;
}
}

This is the proper OOP way, which brings many advantages for testing, extending, and readability. If you use any framework with a Dependency Injection Container (e.g. Laravel), it can provide all the dependencies to your classes automatically. This is why you don’t see that many occurrences of the new keyword in the Laravel projects that are correctly written.

这是正确的OOP方法,为测试,扩展和可读性带来许多优势。 如果您将任何框架与依赖注入容器(例如Laravel)一起使用,它可以自动将所有依赖项提供给您的类。 这就是为什么您看不到Laravel项目中正确地编写了许多关键字的情况。

3.违反“不说,不问”原则 (3. Violation of Tell, Don’t Ask Principle)

Object-oriented programming teaches us about classes. It might not be clear at first how to use the internal class data from the outside. It may seem natural to have the class provide its data, edit it, and put back. For example:

面向对象的程序设计教我们有关类的知识。 起初不清楚如何从外部使用内部类数据。 让类提供其数据,对其进行编辑并放回去似乎很自然。 例如:

class User
{
public $physicalData = [];
public $age = 0;
}$user = new User();user->physicalData['height'] = 180;
user->physicalData['weight'] = 85;$user->age++;

Such an approach might seem intuitive, but in many cases will bring a lot of problems. This is a situation when remembering “Tell, don’t ask” principle is handy. For our class, the rule means, that we should not ask the class for the data, then manipulate them outside of the class. It is much better to tell the class what to do with the data. Here is the solution for the above example:

这种方法看似直观,但在许多情况下会带来很多问题。 记住“ 告诉,不要问 ”原理很方便的情况。 对于我们的班级,规则意味着, 我们不应向班级索要数据 ,而应在班级外操作数据 。 最好告诉班级如何处理数据 。 这是上述示例的解决方案:

class User
{
private $physicalData = [];
private $age = 0; public function increaseAge()
{
$this->age++;
} public function getAge()
{
return age;
} public function setPhysicalData(string $parameter, $value)
{
$this->physicalData[$parameter] = $value;
} public function getPhysicalData($parameter)
{
return array_key_exists($parameter, $this->physicalData)
? $this->physicalData[$parameter]
: null;
}
}$user = new User();user->setPhysicalData('height', 180);
user->setPhysicalData('weight', 85);$user->increaseAge();

In the second example, we are telling the class to manipulate the data for us. There is a significant benefit of following this rule. Our class will most likely keep the interface the same. The class client should not care about the internal structure. The client want’s to, for example, store some data, e.g. weight, height, etc. It doesn’t need to know, how the data is actually handled by the class. It can be a class, array, separate variables, etc. It may even be a file on the disk.

在第二个示例中,我们告诉类为我们操纵数据。 遵循此规则有很大的好处。 我们的课程很可能将接口保持不变。 类客户不应该关心内部结构。 客户希望例如存储一些数据,例如重量,身高等。不需要知道类实际如何处理数据。 它可以是类,数组,单独的变量等。甚至可以是磁盘上的文件。

To make the above example even better, we can split the data into separate variables. It will allow us to introduce type safety and be more explicit about what the functions do.

为了使上述示例更好,我们可以将数据拆分为单独的变量。 这将使我们能够引入类型安全性,并更明确地说明函数的作用。

class User
{ private $height;
private $weight; public function setHeight(int $height)
{
$this->height = $height;
} public function setWeight(float $weight)
{
$this->weight = $weight;
}
… getters and other code
}

4.代码难以阅读 (4. Code is Hard to Read)

I always tell my clients, who are typically non-programmers, that they should be able to somewhat read the code. Either the programmer uses a lot of comments, or he/she splits the code into multiple functions with descriptive naming.

我总是告诉我的客户(通常是非程序员),他们应该能够阅读一些代码。 程序员要么使用大量注释,要么他/她使用描述性命名将代码拆分为多个功能。

(Example)

Compare these two versions of the code, and answer yourself which one is easier for reading.

比较这两个版本的代码,并回答自己哪个更容易阅读。

public function login($email, $user)
{

if(preg_match("/^([a-z0–9\+_\-]+)(\.[a-z0–9\+_\-]+)*@([a-z0– 9\-]+\.)+[a-z]{2,6}$/ix", $email) {
Auth::login($user);
}

}

or

要么

public function login($email, $user)
{

if($this->isEmailValid()) {
Auth::login($user);
}

}private function isEmailValid($email) : bool
{
return (preg_match("/^([a-z0–9\+_\-]+)(\.[a-z0–9\+_\-]+)*@([a- z0–9\-]+\.)+[a-z]{2,6}$/ix", $email));
}

We may feel like creating short functions makes no sense. Why not put the code into the main function? However, splitting your code pays off with improved readability — not only for your teammates and testers, but even for yourself when you look at the code after several months.

我们可能觉得创建短函数没有意义。 为什么不将代码放入主函数中? 但是,拆分代码可以提高可读性,不仅对您的队友和测试人员如此,而且对于几个月后查看代码的人也是如此。

(Example)

Often, we write lines of code that follow our thoughts, and we don’t give the idea a little bit more thinking. It is very common to write something like this:

通常,我们按照自己的想法编写代码行,而我们对此想法不多加考虑。 像这样写是很常见的:

public function login($email, $user)
{
if($this->isEmailValid()) {
if(!$this->registerUser($email)) {
echo('Registration error');
}
} return 'Error';
}

I have seen code with 5–7 nested levels of if, foreach, while, and more ifs. For such complex nested constructions, we can conclude that it is impossible to understand it in a reasonable time. However, many times, we can effectively reduce the number of nested conditions simply by using negative logic.

我看过的代码具有5–7个嵌套的ifforeachwhile和更多ifs嵌套级别。 对于这样复杂的嵌套结构,我们可以得出结论,不可能在合理的时间内理解它。 但是,很多时候,我们可以简单地使用负逻辑来有效地减少嵌套条件的数量。

We can write the above example like this:

我们可以这样写上面的例子:

public function login($email, $user)
{
if(!$this->isEmailValid()) {
return 'Error';
} if(!$this->registerUser()) {
echo('Registration error');
}
}

By simply changing the first condition into a negative, we eliminated one nested level. The code is now much easier to read.

通过简单地将第一个条件更改为负值,我们消除了一个嵌套级别。 现在,该代码更容易阅读。

5.复制和粘贴 (5. Copy and Paste)

Whenever you want to use copy and paste, immediately stop clicking and think. Do you really need to copy the code? Isn’t there a better way? Code reusability is one of the key benefits of OOP. DRY principle — Don’t Repeat Yourself — should be always in our mind.

每当您要使用复制和粘贴时,请立即停止单击并思考。 您真的需要复制代码吗? 有没有更好的办法? 代码可重用性是OOP的主要优势之一。 DRY原则-不要重复自己-我们应该永远记住。

I have seen a website with around 30 pages, where every single page had the same HTML header. What would happen if the client decides to change the favicon? Well, the programmers would need to edit 30 files! Any manual work is very prone to omitting something. It is almost certain that while editing 30 pages, something distracts you, and one or more pages remain un-updated. Simply create a separate header and include it into all the other files!

我看过一个网站,该网站大约有30页,其中每个页面都有相同HTML标头。 如果客户决定更改图标,将会发生什么? 好吧,程序员将需要编辑30个文件! 任何手动工作都非常容易遗漏。 几乎可以肯定的是,在编辑30页时,某些事情会分散您的注意力,并且一页或多页仍保持未更新状态。 只需创建一个单独的标题并将其包含在所有其他文件中即可!

6.代码太长 (6. Code Too Long)

Programmers often write code as they think about it. It is a natural way of coding that leads to long functions and spaghetti code. Three simple tips will make your code much easier to read. First, don’t write longer functions than the height of your monitor is. Simply, if you need to scroll within a single function, it is most likely way too long. Second, avoid nested cycles and conditions. If you need more than three levels of nesting, the code needs rethinking. It can be most likely written better. Third, the function description should not use “and” or at least not too many of them.

程序员经常在思考时编写代码。 这是一种自然的编码方式,可导致较长的功能和意大利面条式代码。 三个简单的技巧将使您的代码更容易阅读。 首先,不要编写比显示器高的功能。 简而言之,如果您需要在单个功能中滚动,则很可能太长了。 其次,避免嵌套循环和条件。 如果需要三个以上级别的嵌套,则需要重新考虑代码。 它很可能写得更好。 第三,函数描述中不应使用“ ”,或者至少不要使用太多。

Recently I saw a function that received input data and validated it and created models and stored them and sent notification emails. Just too many “ands”. Remember SRP — the Single Responsibility Principle.

最近,我看到了一个函数,该函数接收输入数据并对其进行验证,并创建模型,存储它们并发送通知电子邮件。 太多的“ and ”。 记住SRP- 单一责任原则

7.数组而不是类 (7. Arrays Instead of Classes)

As we write code, we tend to save time using trivial solutions even if they are not the best from the OOP perspective. One of such intuitive concept is using an array in case we need to return more values. Like this:

在编写代码时,即使从OOP角度来看并不是最佳解决方案,我们还是倾向于使用琐碎的解决方案来节省时间。 这种直观的概念之一是在需要返回更多值的情况下使用数组。 像这样:

(Example)

public function login($email)
{
if(!$this->isEmailValid()) {
return [
'error' => true,
'message' => 'Invalid email',
'code' => 400
];
}
}

PHP doesn’t care what an array contains. It can be anything — numbers, string, classes, another array of anything. That is another code smell. If an array contains several different types, it probably should be a class. Arrays used instead of classes are extremely difficult to extend. They do not allow any functionality, just carry some data.

PHP不在乎数组包含什么。 它可以是任何东西-数字,字符串,类或任何其他数组。 那是另一种代码的味道。 如果数组包含几种不同的类型,则它可能应该是一个类。 用数组代替类很难扩展。 它们不允许任何功能,仅携带一些数据。

In our example, we may want to test the result for error like this:

在我们的示例中,我们可能要测试结果是否存在如下错误:

$loginStatus = $this->login('my@email.com');if($loginStatus->haserror()) {
…do something
}

Because an array cannot do that, we will be forced to always test a specific array item like this:

因为数组无法做到这一点,所以我们将不得不始终像这样测试特定的数组项:

$loginStatus = $this->login('my@email.com');if($loginStatus['error']) {
…do something
}

The array’s items now cannot be renamed, it might not be obvious what it actually does and we have very limited options for extensions. On top of that, every attempt to update the error object will be a real pain. We may even end up having several different error objects throughout the code.

现在,该数组的项目无法重命名,它的实际作用可能并不明显,而且扩展选项非常有限。 最重要的是,每次尝试更新错误对象都是一个真正的痛苦。 在整个代码中,我们甚至可能最终遇到几个不同的错误对象。

My experience tells me, that whenever I fall into this “it is just a simple array” trap, it will kick me in the back very soon. My recommendation is not to be lazy to create simple classes. We keep the code easier to read, the Open-Closed principle will be followed, and we definitely save a lot of work for the future.

我的经验告诉我,每当我陷入这个“ 只是一个简单的数组 ”陷阱时,它将很快将我踢倒。 我的建议是不要懒于创建简单的类。 我们使代码更易于阅读,将遵循“ 开放-封闭”原则 ,并且我们无疑为将来节省了很多工作。

What are some of the best practices you follow in coding? Share in the comments!

您在编码中遵循的一些最佳做法是什么? 分享评论!

翻译自: https://medium.com/better-programming/what-are-code-smells-in-php-oop-approach-for-cleaner-code-c9729232dc5f

oop编程方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值