簡體   English   中英

在每次函數調用時檢查並拋出異常

[英]Check and throw exception on every function call

我正在用PHP編寫一個函數,將給定其URL的圖像保存到本地文件。 這是我想出的:

private function retrieve_image_url($image_url, $upload_path) {
    $img_data = @file_get_contents($image_url);
    if ($img_data === false) {
      throw new ImageRetrieverException('Invalid image source: '. $image_url);
    }
    $file = @fopen($upload_path, "w+");
    if ($file === false) {
      throw new ImageRetrieverException('Cannot open for writting: '. $upload_path);
    }
    if (fwrite($file, $img_data) === false) {
      throw new ImageRetrieverException('Writing failed: '. $result);
    }
    if (fclose($file) === false) {
      throw new ImageRetrieverException('Cannot close: '. $file);
    }
}

您可以看到該函數看起來過於復雜/難以閱讀,因為我正在檢查每個函數調用並拋出異常。 這樣做是不好的做法嗎?

我想您只發布了方法retrieve_image_url一部分。

讓我們談談那段代碼。

是否有“不良做法”?

這取決於。 通常,“良好做法”是關於代碼應如何的提示,但不是強制性的。
如果您對所有代碼都遵循所有良好實踐,那么您可能會引入過度設計,而在大多數情況下則不需要。 (這是一個不好的做法,哈哈。看看YAGNI

什么時候需要好的做法?

在將來代碼將成為高級維護(修改,刪除等)的一部分時,通常需要良好實踐。 但是,如果您的代碼永遠都是這樣,那么現在您將其埋葬,明天便會忘記它,那么可能就不需要“良好實踐”。

同樣,如果您的代碼將要與其他人(您的工作同事等)共享,則是遵循良好做法的好步驟,以使您的同事輕松理解並輕松修改代碼(如果要修改)。

我的代碼違反了一些好的做法

乍一看,您的代碼插入了一些循環復雜性

這也違反了開放/封閉原則 ,即您必須開放才能進行擴展,而不能進行修改。

為什么該方法是私有的? 當將要從類中的多個位置調用這些方法時,我經常使用私有方法。 如果僅從一個位置調用此方法,則最好內聯調用該方法的位置。

因為我的代碼會發生很大變化,並且我的老板整天都在更改要求,而我的同事需要理解我的代碼並能夠對其進行更改,我如何才能遵循良好的做法?

我認為,我會將您的代碼引入單元測試。
測試將提示什么是壞的,什么是好。
這聽起來很瘋狂,但是隨着時間的流逝,您將學會“聽到測試對您的代碼所說的話”。

但是首先它需要進行一些集成測試,這是一個安全網,可以確認我們的重構沒有破壞代碼。

建議的解決方案

首先,我們對所有異常情況和有效情況進行集成測試:

public class retrieveImageUrl extends PHPUnit_Framework_TestCase
{
    public static function casesProvider()
    {
        return array(
            array("bad URL", "Bad Upload PATH"), //Keep adding all the invalid cases here
            array("replace here with good URL", "Bad upload PATH"), //etc etc
        );
    }
    /**
     * @expectedException ImageRetrieverException
     * @dataProvider casesProvider
    */
    public function testRetrieveImageThrowsInvalidImageSource($url, $path) {
          $yourClass = new YourClass();
          $yourClass->retrieve_image_url($url, $path);
    }


    public function testRetrieveImageValidCase() {
          $yourClass = new YourClass();
          $yourClass->retrieve_image_url("replace with good url", "replace with good path");
    }
}

由於該方法是私有的,因此測試將失敗。 我們將該方法公開(這樣我們就可以對其進行測試)

public function retrieve_image_url($image_url, $upload_path) {
    $img_data = @file_get_contents($image_url);
    if ($img_data === false) {
      throw new ImageRetrieverException('Invalid image source: '. $image_url);
    }
    $file = @fopen($upload_path, "w+");
    if ($file === false) {
      throw new ImageRetrieverException('Cannot open for writting: '. $upload_path);
    }
    if (fwrite($file, $img_data) === false) {
      throw new ImageRetrieverException('Writing failed: '. $result);
    }
    if (fclose($file) === false) {
      throw new ImageRetrieverException('Cannot close: '. $file);
    }
}

然后,我們使用復合模式遵循開/關原則。

我們創建一個“ Validator”類,它將驗證您實際代碼中的兩個約束。

class imageValidator implements InterfaceValidator {
    private $validators;

    public __construct(array $validators = array()) {
        $this->validators = $validators;
    }

    public function validate($file = null, $imgData = null) {
        foreach($this->validators as $validator) 
            $validator->validate($file, $imgData);
    }
}

我們創建的接口將成為所有驗證器的實現。

interface imageInterfaceValidator {
    public function validate($file = null, $imgData = null);
}

接下來是查看驗證方法輸入的約束:

$img_data === false -> throw new ImageRetrieverException('Invalid image source: '. $image_url);

$file === false -> throw new ImageRetrieverException('Cannot open for writting: '. $upload_path);

並將每一個放入實現驗證器接口的類中。 例如,第一個驗證器將是:

class validatorImageFalse implements imageInterfaceValidator {
    public function validate($file = null, $imgData = null) {
        if($img_data === false) {
            throw new ImageRetrieverException('Invalid image source: '. $image_url);
        }
    }
}

依此類推,還有其他限制。

完成代碼后,您的代碼應大致如下所示:

public function retrieve_image_url($image_url, $upload_path) {
    $validatorComposite = new imageValidator(array(/** Put here all the validators classes **/));
    $img_data = @file_get_contents($image_url);
    $file = @fopen($upload_path, "w+");
    $validatorComposite->validate($file, $imgData);

    if (fwrite($file, $img_data) === false) {
      throw new ImageRetrieverException('Writing failed: '. $result);
    }
    if (fclose($file) === false) {
      throw new ImageRetrieverException('Cannot close: '. $file);
    }
}

我認為這將是提高可維護性的一種方式。 因為如果將來會出現這種情況,例如,將來您需要在圖像URL中添加一個正則表達式檢查。 您只需要添加另一個驗證器。

接下來是應用控制反轉

public function retrieve_image_url($image_url, $upload_path, imageInterfaceValidator $validatorComposite) {
    $img_data = @file_get_contents($image_url);
    $file = @fopen($upload_path, "w+");
    $validatorComposite->validate($file, $imgData);

    if (fwrite($file, $img_data) === false) {
      throw new ImageRetrieverException('Writing failed: '. $result);
    }
    if (fclose($file) === false) {
      throw new ImageRetrieverException('Cannot close: '. $file);
    }
}

就是這樣。 您將看到集成測試不斷通過,這非常好。

沒關系。

Symfony對於文件系統具有類似的代碼: https : //github.com/symfony/Filesystem/blob/master/Filesystem.php :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM