[英]Elegant way of chaining checks in PHP
假設您有一個常規驗證 function 並且您必須對輸入進行幾次子檢查。每個子檢查還返回 boolean。 看這個虛構的:
private function hasValidContent(string $content) : bool
{
$isValid = false;
$isValid = $this->hasMoreThanOneChar($content);
$isValid = $this->hasValidCharacters($content);
$isValid = $this->hasCapitalLetters($content);
...
return $isValid;
}
當然,上面的代碼不會起作用,因為每次下一次檢查都會覆蓋前一次的評估。
但是,當第一次檢查導致false
時,如何停止進一步檢查? 例如,如果它的內容不超過一個字符,那么它應該在hasMoreThanOneChar
function 之后停止並立即將hasValidContent
方法返回為 false。
是的,當然,您可以在每個方法調用后檢查表達式是否變為 false,但這很尷尬,而且開銷和重復很多。
如果有數千個||
一個人做的話,感覺真的很難看。 或&&
喜歡
return $this->checkA($content)
&& $this->checkB($content)
&& $this->checkC($content)
...;
再表達幾句后,可讀性會受到影響。
另一個經常提到的方法可能是使用異常
private function hasValidContent(string $content) : bool
{
try {
$this->hasMoreThanOneChar($content);
$this->hasValidCharacters($content);
$this->hasCapitalLetters($content);
...
}
catch {
return false;
}
return true;
}
private function hasMoreThanOneChar(string $string) : void {
if(count($string) < 2 ) {
throw new HasNotMoreThanOneCharException(...)
}
}
但我也認為這不是一個優雅的解決方案,因為驗證東西並沒有什么特別之處。
所以我的問題:
有什么優雅的模式嗎? 我可以搜索任何關鍵字嗎?
創建一個包含您要執行的所有驗證的數組。 數組的每個元素都必須是可調用的。 迭代並提前退出。 如果所有驗證者都通過了,那就很好。
private function hasValidContent(string $content) : bool
{
$validators = [
// instance method
[$this, 'hasMoreThanOneChar'],
// callable class
new class {
public function __invoke(string $content)
{
return ...;
}
},
// anonymous function
function (string $content): bool {
return ...;
}
];
foreach ($validators as $validate) {
if (!$validate($content)) {
return false;
}
}
return true;
}
一個建議可以是讓驗證方法拋出Exception
而不是返回true
和false
(成功時不返回任何內容)。
例如:
Class Validator {
public static function assertString($string) {
if (!is_string($string)) {
throw new Exception('Type mismatch.');
}
}
public static function assertStringLength($string, $min, $max) {
if (strlen($string) < $min || strlen($string) > $max) {
throw new Exception('String outside of length boundaries.');
}
}
}
您的代碼將如下所示:
try {
Validator::assertString($string);
Validator::assertStringLength($string, 4, 50);
} catch (Exception $e) {
// handle invalid input
}
請注意,我的函數是 static,在這種情況下有意義,但不一定總是如此。
我推斷您正在尋找責任鏈模式。
例如,假設您有三個類:Locks、Alarm 和 Lights,它們檢查家庭的狀態。
abstract class HomeChecker {
protected $successor;
public abstract function check(Home $home);
public function succeedWith($successor) {
$this->successor = $successor;
}
public function next(Home $home) {
$this->successor->check($home);
}
}
class HomeStatus {
public $locked = true;
public $alarmOn = true;
public $lightsOff = true;
}
class Locks extends HomeChecker {
public function check(Home $home) {
if(!$home->locked) {
throw new Exception('The doors are not locked'); // or basically whatever you want to do
}
$this->next($home);
}
}
class Alarm extends HomeChecker {
public function check(Home $home) {
if(!$home->alarmOn) {
throw new Exception('The Alarms are not on'); // or basically whatever you want to do
}
$this->next($home);
}
}
class Lights extends HomeChecker {
public function check(Home $home) {
if(!$home->lightsOff) {
throw new Exception('The lights are still on!'); // or basically whatever you want to do
}
$this->next($home);
}
}
$locks = new Locks();
$alarm = new Alarm();
$lights = new Lights();
$locks->succeedWith(new Alarm); // set the order in which you want the chain to work
$alarms->succeedWith(new Lights);
$locks->check(new HomeStatus);
所有三個類:Locks、Lights 和 Alarm 都擴展了一個名為HomeChecker的 class,它基本上具有一個抽象檢查方法,以確保子類確實實現了該方法和兩個名為successWith和next的方法。 succeedWith 方法用於設置一個后繼者,如果當前正在執行的方法返回 true(意味着沒有錯誤),則該后繼者將被調用。 而下一個方法本質上是調用鏈接起來的下一個方法。
使用這一行$locks->check(new HomeStatus);
,我們開始鏈,如果鎖class 的檢查方法中的檢查失敗,則會拋出錯誤並停止執行,否則它將調用HomeChecker class 的下一個方法,最終將調用下一個的檢查方法設定的class
$locks->succeedWith(new Alarm); // set the order in which you want the chain to work
$alarms->succeedWith(new Lights);
在這些行中。
所以通過這種方式,我們可以非常優雅地進行鏈式檢查。 我希望這能幫到您。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.