by 尤川豪
Entity太胖
軟體測試:鏡射結構
Controller太髒
沒有人測系統
把日期、金額、名稱之類的呈現(presentation)邏輯抽離出來!
class Article
{
public function getDate(){/*...*/}
public function getTaiwaneseDateTime(){/*...*/}
public function getWesternDateTime(){/*...*/}
public function getTaiwaneseDate(){/*...*/}
public function getWesternDate(){/*...*/}
}
class ArticlePresenter
{
protected $article;
public function __construct(Article $article)
{
$this->article = $article;
}
public function getTaiwaneseDateTime(){
return date('Y-m-d', $this->article->created_at);
}
public function getWesternDateTime(){/*...*/}
public function getTaiwaneseDate(){/*...*/}
public function getWesternDate(){/*...*/}
}
class Article
{
public function present()
{
return new ArticlePresenter($this);
}
}
$article->present()->getTaiwaneseDate();
把查詢(query)的邏輯,也就是取得entity的各種方式抽離出來!
class UserRepository
{
public function getPopularWomen()
{
return User::where('votes', '>', 100)
->whereGender('W')
->orderBy('created_at')
->get();
}
public function getActiveUsers(){}
public function getPaidUsers(){}
}
$repository = new UserRepository();
$users = $repository->getPopularWomen();
把參數驗證(validation)的邏輯(例如字串長度、日期、金額大小)抽離出來!
$validation = Validator::make(
array(
'title' => $title,
'content' => $content,
),
array(
'title' => array( 'required', 'alpha_dash' ),
'content' => array( 'required' ),
)
);
return $validation->passes();
class ArticleForm
{
protected $validationRules = [
'title' => array( 'required', 'alpha_dash' ),
'content' => array( 'required' )
];
protected $validator;
public function isValid($input)
{
$this->validator = Validator::make($input, $this->validationRules);
return $this->validator->passes();
}
public function getErrors()
{
return $this->validator->errors();
}
}
$form = new ArticleForm();
if ( ! $form->isValid(Input::all()) ){
return Redirect::back()->with( [ 'errors' => $form->getErrors() ] );
}
// Passed the validation.
// Create the article.
Entity太胖
軟體測試:鏡射結構
Controller太髒
沒有人測系統
class TranslatorAssign
{
protected $googleDrive;
public function __construct($googleDrive)
{
$this->googleDrive = $googleDrive;
}
public function execute($user, $document)
{
$user->doSomething();
$this->googleDrive->backup($document->file_id);
}
}
//寫一個有backup方法的假類別GoogleDriveMock
$service = new TranslatorAssign(new GoogleDriveMock());
$service->execute($user, $document);
$this->assertEquals(Document::ASSIGNED_STATUS, $document->status);
/* 用test framework支援的mocks也可以
$stub = $this->getMockBuilder('GoogleDrive')
->getMock();
$stub->method('backup')
->willReturn('foo');
$service = new TranslatorAssign($stub);
*/
$service = new TranslatorAssign(new GoogleDrive());
$service->execute($user, $document);
//Return successful page
$document = Document::find(Input::get('id'));
if(Input::get('role') == 'translator'){
$document->doSomethingForTranslator();
$document->doAnotherThingForTranslator();
}else if(Input::get('role') == 'editor'){
$document->doSomethingForEditor();
$document->doAnotherThingForEditor();
}
//Return successful page
class GetAccessPermission
{
//有想避開的dependency,就用constructor injection
public function __construct(/*...*/)
{
//...
}
public function execute($document, $role)
{
if($role == 'translator'){
$document->doSomethingForTranslator();
$document->doAnotherThingForTranslator();
}else if($role == 'editor'){
$document->doSomethingForEditor();
$document->doAnotherThingForEditor();
}
}
}
$service = new GetAccessPermission();
$service->execute($document, 'translator');
$this->assertTrue($document->has_translator);
$this->assertTrue($document->blah_blah);
$document = Document::find(Input::get('id'));
$service = new GetAccessPermission();
$service->execute($document, Input::get('role'));
//Return successful page
class CheckoutBill
{
public function execute($user, $order, $product)
{
//...
}
}
$calcDueDate = new CalcDueDate();
$calcPrice = new CalcPrice();
$calcDiscount = new CalcDiscount();
$calcDueDate->execute($order);
$calcPrice->execute($order);
$calcDiscount->execute($order);
class QuotationManager
{
protected $calcDueDate;
protected $calcPrice;
protected $calcDiscount;
public __construct()
{
$this->calcDueDate = new CalcDueDate();
$this->calcPrice = new CalcPrice();
$this->calcDiscount = new CalcDiscount();
}
public function execute($order)
{
$this->calcDueDate($order);
$this->calcPrice($order);
$this->calcDiscount($order);
}
}
$quotation = new QuotationManager();
$quotation->execute($order);
/MyApp
/GlossarySystem
/Manager.php
/Analyzer.php
/Generator.php
/Parser.php
/Splitter.php
/Entity
/Text.php
/Segment.php
把其他公司也能使用、
概念上獨立於當前專案的程式碼抽離出來!
/MyApp
/...
/...
/Howtomakeaturn(Github帳號)
/MyPackage1
/...
/MyPackage2
/...
/MyPackage3
/...
Entity太胖
軟體測試:鏡射結構
Controller太髒
沒有人測系統
tests的檔案結構,與App核心一模一樣
/MyApp
/Order
/Order.php
/OrderRepository.php
/Product
/Product.php
/ProductPresenter.php
/Service
/TranslatorAssign.php
/GetAccessPermission.php
/CheckoutBill.php
/...
tests/MyApp
/Order
/OrderTest.php
/OrderRepositoryTest.php
/Product
/ProductTest.php
/ProductPresenterTest.php
/Service
/TranslatorAssignTest.php
/GetAccessPermissionTest.php
/CheckoutBillTest.php
/...
Entity太胖
軟體測試:鏡射結構
Controller太髒
沒有人測系統
檔案結構範例
延伸閱讀