Contenu connexe Similaire à PHP 良好實踐 (Best Practice) (20) PHP 良好實踐 (Best Practice)5. Naming 名詞單複數
// 複數名詞 ResultSet / Collection
$users = User::search(1);
// 單數名詞 Row
$article = BlogArticle::find(1);
6. 變數提煉
foreach ($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary();
$experience = $employee->getExperience();
$githubLink = $employee->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
✤ 當有進⼀步運⽤需求時
7. 變數提煉
foreach ($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary();
$experience = $employee->getExperience();
$githubLink = $employee->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];
render($data);
}
foreach ($employees as $employee) {
render([
$employee->calculateExpectedSalary()
$employee->getExperience(),
$employee->getGithubLink(),
]);
}
✤ 當有進⼀步運⽤需求時
✤ 但,只有賦值就不建議提煉
8. 變數提煉
foreach ($employees as $employee) {
render([
$employee->calculateExpectedSalary()
$employee->getExperience(),
$employee->getGithubLink(),
]);
}
$bouns = 1.25;
foreach ($employees as $employee) {
$expectedSalary = $employee->calculateExpectedSalary() * $bouns;
render([
$expectedSalary,
$employee->getExperience(),
$employee->getGithubLink(),
]);
}
✤ 當有進⼀步運⽤需求時
✤ 假設要後處理(額外運算)
9. 變數提煉
✤ 特殊語意不易理解的時候
$user = User::find(1);
if (!$user->friends->friends_feeds['12345']->friends_comments->count()) {
echo 'No comments.'
}
10. 變數提煉
✤ 特殊語意不易理解的時候
$user = User::find(1);
$friends_comments_count = $user->friends->friends_feeds['12345']->friends_comments->count();
if (!$friends_comments_count) {
echo 'No comments.'
}
$user = User::find(1);
if (!$user->friends->friends_feeds['12345']->friends_comments->count()) {
echo 'No comments.'
}
12. 常數提煉
class CardAuthor
{
// ....
public function checkAdmin()
{
return (2 === $this->role);
}
}
class CardAuthor
{
// ....
const ROLE_GENERAL = 0; // ⼀一般作者
const ROLE_EDITOR = 1; // 內容管理理員
const ROLE_ADMIN = 2; // 者總管理理員
public function checkAdmin()
{
return (self::ROLE_EDITOR === $this->role);
}
}
✤ 物件的限制特性
✤ 跟著類別⾛,語易化更佳
14. $member = Member::find(1);
if (5 < $member->day_logs) {
throw new Exception('該名員⼯工超時⼯工作!!');
}
$workdays_per_week_limit = 5;
if ($workdays_per_week_limit < $member->day_logs) {
throw new Exception('該名員⼯工超時⼯工作!!');
}
變/常數提煉 ✤ 特殊⽤意的參數都建議提煉
➡ 能被改就⽤變數 ($variable)
➡ 不能被改就⽤常數 (constant)
15. 函式 - ⾸則要務 − 簡短
public function checkAlbumStatus()
{
if ('ok' === $this->album->status) {
return true;
}
return false;
}
16. 函式 - ⾸則要務 − 簡短
public function checkAlbumStatus()
{
if ('ok' === $this->album->status) {
return true;
}
return false;
}
public function checkAlbumStatus()
{
$album_status = $this->album->status;
return ('ok' === $album_status) ? true : false;
}
…
17. 函式 - ⾸則要務 − 簡短
public function checkAlbumStatus()
{
$is_ok = ('ok' === $this->album->status);
return $is_ok;
}
…
18. 函式 - ⾸則要務 − 簡短
public function checkAlbumStatus()
{
$is_ok = ('ok' === $this->album->status);
return $is_ok;
}
…
public function checkAlbumStatus()
{
return ('ok' === $this->album->status);
}
19. ✤ set 設定某個 property
✤ get 設定某個 property
✤ is/has 通常判斷式 boolean
✤ check 做某種條件檢查 boolean, void
✤ filter 過濾, input/output 不⾒見見得⼀一樣 mixed
✤ validate 驗證器資料是否符合預期 boolean, void, throw exception
函式 - 動詞起⼿式
✤ can
✤ should
20. 函式 - 動詞起⼿式
/**
* getUser 取得使⽤用者
*
* @param int $id
* @return UserRow
*/
function getUser($id)
{
// implement
}
/**
* setAge 設定使⽤用者年年齡
*
* @param UserRow
* @return boolean
*/
function setAge($user)
{
// implement
}
/**
* isAdmin 判斷是否為管理理者
*
* @param UserRow
* @return boolean
*/
function isAdmin($user)
{
// implement
}
/**
* checkLogin 檢查免費的時效
*
* @return void
*/
function checkFreeExpired()
{
// implement
}
set
get is
check
21. 函式 - 動詞起⼿式
/**
* validateCSRFToken 檢查 CSTF Token
*
* @thorw Exception
* @return void
*/
function validateCSRFToken()
{
if (...) {
throw new Exception("Don't Hack Me.");
}
// return true; //(optional)
}
<?php
class UserPreference
{
const SERVICE_WHITE_LIST = [
'PIXNET',
'STYLEME',
'PIXWALLET'
];
/**
* filterRegisterService 過濾註冊服務類別
*
* @param string $service
* @return string
*/
public function filterRegisterService($service)
{
if (in_array($service, self::SERVICE_WHITE_LIST)) {
return $service;
}
return "kylinyu.win";
}
}
filter
validate
23. 函式 - 只做⼀件事
- 難以組成
- 難以測試
- 難以理解
- 容易測試
- 容易重構
- 結構清晰
當⼀個函式
做超過⼀件事的時候...
當⼀個函式
拆分到只剩⼀個功能時...
24. 函式 - 只做⼀件事
function createArticle($title, $is_draft = true)
{
if ($is_draft) {
touch('./temp/' . $title);
} else {
touch('./article/' . $title);
}
}
✤ 不⽤ Flag 做參數
25. 函式 - 只做⼀件事
function createArticle($title, $is_draft = true)
{
if ($is_draft) {
touch('./temp/' . $title);
} else {
touch('./article/' . $title);
}
}
function createArticle($title)
{
touch('./temp/' . $title);
}
function createDraftArticle($title)
{
touch('./article/' . $title);
}
✤ 不⽤ Flag 做參數
27. 能⾒度Visibility
class User
{
public $name;
public function __construct($name)
{
$this->name = $name;
}
}
$user = new User('Win Yu');
echo 'User name: ' . $user->name;
// User name: Win Yu
class User
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
$user = new User('Win Yu');
echo 'User name: ' . $user->getName();
// User name: John Doe
✤ 沒必要對外開放的就不要開 public
✤ 使⽤類別時只要看 public 成員就好
29. 封裝條件句
if ('active' === $user->status) {
// do something
}
if ($user->isActive()) {
// do something
}
class User {
// ...
public function isActive()
{
return ('active' === $this->status);
}
}
✤ 更清楚的語意
30. Guard Clause 原則
//define('DEBUG_ENV', true);
/**
* markByENV 根據 env 做標記
*
* @return string
*/
function markByENV()
{
$user = User::find($user_id);
if (DEBUG_ENV) {
$user->mark('staging');
$message = "標記 {$user->name} staging";
} else {
$user->mark('production');
$message = "標記 {$user->name} production";
}
return $message;
}
✤ Early return
✤ 減少巢狀 loop
31. Guard Clause 原則
//define('DEBUG_ENV', true);
/**
* markByENV 根據 env 做標記
*
* @return string
*/
function markByENV()
{
$user = User::find($user_id);
if (DEBUG_ENV) {
$user->mark('staging');
$message = "標記 {$user->name} staging";
} else {
$user->mark('production');
$message = "標記 {$user->name} production";
}
return $message;
}
//define('DEBUG_ENV', true);
/**
* markByENV 根據 env 做標記
*
* @return sting
*/
function markByENV()
{
$user = User::find($user_id);
if (DEBUG_ENV) {
$user->mark('staging');
return "標記 {$user->name} staging";
}
$user->mark('production');
return "標記 {$user->name} production";
}
✤ Early return
✤ 減少巢狀 loop
34. 查表法
/**
* getFruitByColor ⽤用顏⾊色取得⽔水果名稱
*
* @param string $color
* @return string
*/
function getFruitByColor($color)
{
if ('紅⾊色' === $color) {
return '蘋果';
} elseif ('綠⾊色' === $color) {
return '芭樂';
} elseif ('紫⾊色' === $color) {
return '葡萄';
}
return $color;
}
✤ 簡化 if/else 可讀性更⾼
35. 查表法
/**
* getFruitByColor ⽤用顏⾊色取得⽔水果名稱
*
* @param string $color
* @return string
*/
function getFruitByColor($color)
{
if ('紅⾊色' === $color) {
return '蘋果';
} elseif ('綠⾊色' === $color) {
return '芭樂';
} elseif ('紫⾊色' === $color) {
return '葡萄';
}
return $color;
}
✤ 簡化 if/else 可讀性更⾼
/**
* getFruitByColor ⽤用顏⾊色取得⽔水果名稱
*
* @param string $color
* @return string
*/
function getFruitByColor($color)
{
$fruit_map = [
'紅⾊色' => '蘋果',
'綠⾊色' => '芭樂',
'紫⾊色' => '葡萄',
];
return isset($fruit_map[$color]) ?
$fruit_map[$color] : color;
}
36. 查表法
/**
* getFruitByColor ⽤用顏⾊色取得⽔水果名稱
*
* @param string $color
* @return string
*/
function getFruitByColor($color)
{
if ('紅⾊色' === $color) {
return '蘋果';
} elseif ('綠⾊色' === $color) {
return '芭樂';
} elseif ('紫⾊色' === $color) {
return '葡萄';
}
return $color;
}
✤ 簡化 if/else 可讀性更⾼
/**
* getFruitByColor ⽤用顏⾊色取得⽔水果名稱
*
* @param string $color
* @return string
*/
function getFruitByColor($color)
{
$fruit_map = [
'紅⾊色' => '蘋果',
'綠⾊色' => '芭樂',
'紫⾊色' => '葡萄',
];
return isset($fruit_map[$color]) ?
$fruit_map[$color] : color;
}
return $fruit_map[$color] ?? $color;
PHP 7
37. 避免⽤ switch/case
⽽是⽤多型
class Airplane
{
// ...
public function getCruisingAltitude()
{
switch ($this->type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
✤ ⼀個函式做多件事
✤ 等同任由後者隨意增加邏輯於
case 中
38. interface Airplane
{
// ...
public function getCruisingAltitude();
}
class Boeing777 implements Airplane
{
// ...
public function getCruisingAltitude()
{
return $this->getMaxAltitude() -
$this->getPassengerCount();
}
}
class AirForceOne implements Airplane
{
// ...
public function getCruisingAltitude()
{
return $this->getMaxAltitude();
}
}
class Cessna implements Airplane
{
// ...
public function getCruisingAltitude()
{
return $this->getMaxAltitude() -
$this->getFuelExpenditure();
}
}
避免⽤ switch/case
⽽是⽤多型
40. See more
✤ 無瑕程式碼的彩虹七式
✤ https://www.slideshare.net/kylinfish/clean-code-72688451
✤ Clean Code PHP
✤ https://github.com/php-cpm/clean-code-php
✤ PHP The Right Way
✤ https://laravel-china.github.io/php-the-right-way