development:software-architecture:design-patterns:strategy
This is an old revision of the document!
Strategy
Strategy là một mẫu thiết kế hành vi cho phép xác định một họ thuật toán, đưa từng thuật toán vào một lớp riêng biệt thay vì phải dùng nhiều điều kiện if trong một function - khó tiếp cận và bảo trì.
Ví dụ khi chưa dùng Strategy, đoạn code quá nhiều if dẫn đến khó kiểm soát:
function getProviderTransfer($userInfo, $bankInfo = null, $accountNo = null, $options = []) { if(!empty($accountNo) && preg_match('/^(M999)/', $accountNo, $matches) && $bankInfo->code == Bank::BANK_CODE_TECHCOMBANK){ return UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK; } if (!empty($accountNo) && preg_match('/^(966699)/', $accountNo, $matches) && $bankInfo->code == Bank::BANK_CODE_MSB) { return UserTransaction::PROVIDER_TRANSFER_VPBANK; } if (!empty($options['amount']) && is_numeric($options['amount']) && $options['amount'] < 2000) { if ($bankInfo->code == Bank::BANK_CODE_TECHCOMBANK && !Bank::isAccountNoTCB($accountNo)) { return UserTransaction::PROVIDER_TRANSFER_VIETINBANK; } if (in_array($bankInfo->code, [ UserTransaction::PROVIDER_TRANSFER_TPBANK, UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK, UserTransaction::PROVIDER_TRANSFER_VIETINBANK, UserTransaction::PROVIDER_TRANSFER_MBBANK, UserTransaction::PROVIDER_TRANSFER_VPBANK])) { return $bankInfo->code; } return UserTransaction::PROVIDER_TRANSFER_VIETINBANK; } $appSetting = AppSetting::get()->pluck('content','key')->toArray(); $providerDefault = data_get($appSetting, 'PAYOUT_PROVIDER_DEFAULT', UserTransaction::PROVIDER_TRANSFER_VPBANK); if (!empty($userInfo->payout_provider_code)) { $accountName = !empty($options['account_name']) ? $options['account_name'] : ''; if ($userInfo->payout_provider_code == UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK && !Bank::isValidAccountNameByFirstName($accountName)) { return $providerDefault; } if ($userInfo->payout_provider_code == UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK && $bankInfo->code == Bank::BANK_CODE_TECHCOMBANK && !Bank::isAccountNoTCB($accountNo)) { return $providerDefault; } if ($userInfo->payout_provider_code == UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK && !empty($accountNo) && (Bank::checkTypeCard($accountNo) || Bank::hasSpecialCharacter($accountNo) || Phone::isValid($accountNo))){ return $providerDefault; } if ($userInfo->payout_provider_code == UserTransaction::PROVIDER_TRANSFER_TPBANK && !empty($accountNo) && (Bank::checkTypeCard($accountNo) || Bank::hasSpecialCharacter($accountNo) || !is_numeric($accountNo) || Phone::isValid($accountNo))) { return $providerDefault; } $payoutProviderSetting = !empty($userInfo->payout_provider_settings) ? explode(',', $userInfo->payout_provider_settings) : []; if ($payoutProviderSetting && is_array($payoutProviderSetting) && in_array($bankInfo->code, $payoutProviderSetting)) { return $bankInfo->code; } return strtoupper($userInfo->payout_provider_code); } if (!empty($options['amount']) && $options['amount'] >= config('setting.min_citad_amount') && in_array($userInfo->name, config('setting.merchant_citad'))) { return UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK; } if(!is_numeric($accountNo) && !in_array($bankInfo->code, config('bank.bank_is_support_inick'))){ return $providerDefault; } if (!empty($accountNo) && Bank::checkTypeCard($accountNo) && !in_array($bankInfo->code, config('bank.bank_is_support_card'))){ $bankTransferAtm=data_get($appSetting, 'PAYOUT_PROVIDER_ATM', $providerDefault); if($bankTransferAtm==UserTransaction::PROVIDER_TRANSFER_BIDV && $bankInfo->code==Bank::BANK_CODE_MSB) { return $providerDefault; } return $bankTransferAtm; } if (!empty($accountNo) && Phone::isValid($accountNo) && !in_array($bankInfo->code, config('bank.bank_is_support_phone_number'))){ return $providerDefault; } if (!empty($accountNo) && Bank::hasSpecialCharacter($accountNo)) { return $providerDefault; } $transferBankSetting = TransferBankPartner::query() ->where('bank_code', $bankInfo->code) ->first(); if (!$transferBankSetting instanceof TransferBankPartner) { return $providerDefault; } if(in_array($transferBankSetting->provider,[UserTransaction::PROVIDER_TRANSFER_TPBANK,UserTransaction::PROVIDER_TRANSFER_BIDV]) && in_array(Carbon::now()->hour,[23,0])){ return $providerDefault; } if ($transferBankSetting->provider == UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK && $bankInfo->code == Bank::BANK_CODE_TECHCOMBANK && !Bank::isAccountNoTCB($accountNo)) { return $providerDefault; } if ($transferBankSetting->provider == UserTransaction::PROVIDER_TRANSFER_TECHCOMBANK && !empty($accountNo) && (Bank::checkTypeCard($accountNo) || !is_numeric($accountNo) || Phone::isValid($accountNo))) { return $providerDefault; } if ($transferBankSetting->provider == UserTransaction::PROVIDER_TRANSFER_TPBANK && !empty($accountNo) && (Bank::checkTypeCard($accountNo) || !is_numeric($accountNo) || Phone::isValid($accountNo))) { return $providerDefault; } if ($transferBankSetting->provider == UserTransaction::PROVIDER_TRANSFER_MBBANK && !empty($options['amount']) && $options['amount'] > 4000000000) { return $providerDefault; } return $transferBankSetting->provider; }
Ví dụ về Strategy Pattern:
- PaymentMethodInterface.php
<?php namespace App\Services\Payment; interface PaymentMethodInterface { /** * pay * * @return string */ public function pay(): string; }
- CreditCardService.php
<?php namespace App\Services\Payment; /** * CreditCardService */ class CreditCardService implements PaymentMethodInterface { /** * pay * * @return string */ public function pay(): string{ return 'CreditCard'; } }
- ApplePayService.php
<?php namespace App\Services\Payment; /** * ApplePayService */ class ApplePayService implements PaymentMethodInterface { /** * pay * * @return string */ public function pay(): string{ return 'ApplePay'; } }
- GooglePayService.php
<?php namespace App\Services\Payment; /** * GooglePayService */ class GooglePayService implements PaymentMethodInterface { /** * pay * * @return string */ public function pay(): string{ return 'GooglePay'; } }
- PaymentContextService.php
<?php namespace App\Services\Payment; /** * PaymentContextService */ class PaymentContextService { /** * @var PaymentMethodInterface */ private $paymentMethod; /** * __construct * * @param mixed $paymentMethod * @return void */ public function __construct(string $paymentMethod) { $this->paymentMethod = match ($paymentMethod){ 'credit' => new CreditCardService(), 'apple' => new ApplePayService(), 'google' => new GooglePayService(), default => throw new \InvalidArgumentException("You must pass in either credit, apple or google as the payment method.") }; } /** * pay * * @return string */ public function pay(){ return $this->paymentMethod->pay(); } }
- StrategyCommand.php
<?php namespace App\Console\Commands\Test; use App\Services\Payment\PaymentContextService; use Illuminate\Console\Command; /** * Class StrategyCommand * @package Modules\Web\Console\Commands */ class StrategyCommand extends Command { /** * The console command name. * * @var string */ protected $signature = 'test:strategy {paymentMethod}'; /** * The console command description. * * @var string */ protected $description = "Test Cms module command"; public function handle(){ //$result = $this->normal($this->argument('paymentMethod')); $result = $this->strategy($this->argument('paymentMethod')); $this->line($result); } private function strategy($paymentMethod){ $payment = new PaymentContextService($paymentMethod); return $payment->pay(); } /** * normal * * @param mixed $paymentMethod * @return string */ private function normal($paymentMethod){ if('credit' == $paymentMethod){ return 'CreditCard'; } if('google' == $paymentMethod){ return 'GooglePay'; } if('apple' == $paymentMethod){ return 'ApplePay'; } throw new \InvalidArgumentException("You must pass in either credit, apple or google as the payment method."); } }
Tham khảo
development/software-architecture/design-patterns/strategy.1723554735.txt.gz · Last modified: 2024/08/13 13:12 by tungnt