文章转发自专业的Laravel开发者社区,原始连接:learnku.com/laravel/t/3…php
咱们添加到项目的每一行代码,都会增长了它们的复杂性,而且增长了随时会产生bug的可能性。多是在客户开会前几分钟,也多是咱们周末在电影院期间,不在咱们的键盘前。laravel
为了防止那些可怕的状况出现,让咱们经过下面七个技巧,来编写更好的代码:算法
代码只写一次,但会被其余开发人员和您屡次阅读和解释。所以,值得花一些额外的时间来命名这个新的类或方法,所以它的名称显示了它的真实意图或内容。 Let’s compare these two lines. Which one is easier to understand?编程
咱们来比较一下这两行。哪个更容易理解?数组
$evnt->add($req->q);
复制代码
$event->addTickets($request->quantity);
复制代码
第一行有输入错误,add
方法不清楚添加了什么,变量 $req
不够清楚,很难理解 q
是指数量。bash
另外一方面,第二个例子即便对于非开发人员也很容易理解。函数
永远不要低估以有序和一致的方式编写代码的重要性,由于这样可让您更快地发现问题。单元测试
考虑如下两个例子:测试
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i)
{
$code = Code::generate(); }
$this->tickets()->create(
[
'code' => $code,
]);
}
复制代码
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$code = Code::generate();
}
$this->tickets()->create([
'code' => $code,
]);
}
复制代码
两个代码块都有相同的错误:当它们应该建立 N 时,两个代码块都只建立一个 ticket 。可是在哪一个代码块中,您更快地发现了问题?如今想象一下处理格式错误的复杂代码的后果。ui
儘管我們學習演算法的第一章是如何宣告及使用臨時變量,他們仍然會讓代碼的閱讀及維護變得困難。
思考一下下方的例子:
$contact = array();
$contact['firstname'] = $user->first_name;
$contact['surname'] = $user->last_name;
$contact['id'] = $user->id;
$contact_emails = array();
$contact_email = array();
$contact_email['email'] = $user->email;
$contact_emails[] = $contact_email;
$contact['emails'] = $contact_emails;
$this->create('contact', $contact);
复制代码
$contact = [
'id' => $user->id,
'firstname' => $user->first_name,
'surname' => $user->last_name,
'emails' => [
[
'email' => $user->email,
],
],
];
$this->create('contact', $contact);
复制代码
哪個例子更容易理解?
順道一提,使用等號是一個壞習慣。這不僅是違反了 PSR-2,也會讓代碼變得難以維護。
因此,回到我們的第二個例子,這個例子能够藉由去除 code
變量,以行內的寫法來優化:
public function addTickets($quantity)
{
foreach (range(1, $quantity) as $i) {
$this->tickets()->create([
'code' => Code::generate(6),
]);
}
}
复制代码
然而,在某些情況下,使用局部變量能够提升代碼的易讀性,例如:
function calculateCode($price, $quantity, $deliveryCost)
{
$subtotal = $price * $quantity;
if ($subtotal < 30) {
$subtotal += $deliveryCost;
}
return $subtotal;
}
复制代码
可能會比下面這個更清晰:
<?php
function calculateTotal($price, $quantity, $deliveryCost)
{
if ($price * $quantity < 30) {
return $price * $quantity + $deliveryCost;
}
return $price * $quantity;
}
复制代码
若是一份订单低于30元,那么将不包邮,并支付额外的运费, 这个时候咱们应该使用常量来标识,若是订单价格低于30元,那么将支付运费。常量的配置使用以下:
if ($subtotal < DELIVERY_COST_THRESHOLD) {
$subtotal += $deliveryCost;
}
复制代码
翻译代码对照以下:
if ( 订单价格 < 不包邮的价格) {
$当前订单价格 += 运费价格;
}
复制代码
在这个方法中, 咱们展现了使用常量的便捷, 一样咱们也能够在其余项目须要使用的部分重复使用这个常量。
若是咱们须要改变不包邮的规则, 咱们仅仅只须要更新一行常量代码, 既减小重复,同时也减小了在代码中使用固定数字来判断的不肯定性。
许多场景均可以将过长的代码分离成多个小方法,使得每个方法都有不一样的职责。例如:
新方法 getContactInfo
将返回带有用户联系信息的数组:
$this->create('contact', $user->getContactInfo());
复制代码
面向对象编程要求咱们将数据和函数集中在一个地方(类)。咱们将在包含全部用户信息的(
User
模型)中组装包含联系人信息的数组。
再看另外一个例子
$subtotal = $item->price * $quantity;
$subtotal = $this->addDeliveryCost($subtotal);
复制代码
方法 addDeliveryCost
将返回一个交付成本的金额,但前提是该金额不超过设置的阀值,不然将返回原始金额。
如今让咱们删除本地变量并内联代码:
return $this->addDeliveryCost($price * $quantity);
复制代码
声明和使用许多小方法是减小代码中使用临时变量的好方法。
许多承诺你将编写出更好的代码的教程到最后都会使代码变的过于复杂。
若是你正在使用 Laravel 和 Eloquent,这些教程就会告诉你,将下面这段代码放在控制器是错误的:
// Somewhere in UserController.php
User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
复制代码
你应该这样写:
// Somewhere in UserController.php
$this->commandTransport->handleCommand(
new UserCreationCommand(
new UserNameField($request->name),
new UserEmailField($request->email),
new UserPasswordField(bcrypt($request->password)),
)
);
复制代码
在 UserCreationCommandHandler
类中,你一样不能建立 user,由于这违反了 SOLID 原则。你应该使用 repository:
class UserCreationCommandHandler
{
//...
public function handle(UserCreationCommand $command)
{
$this->userRepository->create(
$command->name,
$command->email,
$command->password,
);
}
}
复制代码
最终,在 UserEloquentRepository
类中, 你将最后调用 User::create
:
class UserEloquentRepository implements UserRepository
{
//...
public function create(
UserNameField $name,
UserEmailField $email,
UserPasswordField $password
) {
return User::create([
'name' => $name->getValue(),
'email' => $email->getValue(),
'password' => bcrypt($password->getValue()),
]);
}
}
复制代码
过了一会,客户端要求你向 User 模型添加另外一个字段。
哪一个更简单?哪一个方案更容易出 bug (极可能你就忘记将一个字段从一个方法传入另外一个方法)。
同时,你是否注意到在例2中调用了两次 bcrypt
?因此第二个例子有 bug。
不幸的是,有些接口和类不会阻止你犯错。因此,须要仔细的去测试代码,说到测试代码:
会计师采用一种叫作「复式记帐法」的方式记帐。这种方法要求他们把全部的交易都记录两次。编写单元测试须要咱们编写两次代码,一次定义每个测试:
function test_order_without_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price' => 20]), 5);
$expectedTotal = 20 * 5;
$this->assertSame($expectedTotal, $order->getTotal());
}
function test_order_with_delivery_cost()
{
$order = new Order;
$order->addItem(new Item(['price' => 20]), 1);
$expectedTotal = 20 + DELIVERY_COST;
$this->assertSame($expectedTotal, $order->getTotal());
}
复制代码
第二次编写代码的实现 (这个艰巨的任务就交给你了)。
许多开发人员抱怨这种作法是由于它迫使咱们“加倍工做”,可是经过编写两次代码,咱们减小了以一样的方式犯一样错误的可能性(若是咱们犯两个不一样的错误,测试可能会失败)。这就是为何实现一些单元测试的项目每每会有很小的bug,然而须要好几个小时的调试时间。