我正在用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);
}
}
你可以看到函数看起来太复杂/难以阅读,因为我正在检查并在每个函数调用上抛出异常。这样做是不好的做法吗?
答案 0 :(得分:1)
我想你只发布了retrieve_image_url
方法的一部分。
让我们谈谈那段代码。
这取决于。大多数“良好实践”都是关于代码应该如何的提示,但不是强制性的 如果您遵循所有代码的所有良好实践,您可能会引入过度工程,在大多数情况下不需要。 (这是一个糟糕的做法大声笑。看看YAGNI)
当代码将成为未来高级维护(修改,删除等)的一部分时,通常需要良好的实践。但是,如果你的代码将永远像它的方式,现在你将埋葬并明天忘记它,那么可能不需要“良好实践”。
此外,如果您的代码将与其他人(您的同事等)共享,那么遵循良好实践是一个很好的步骤,以便您的同事可以轻松地理解代码并轻松修改代码(如果要修改它) )。
初看起来,您的代码会插入一些cyclomatic complexity。
它也违反了open/closed principle,它表示你必须开放以进行扩展,但不能进行修改。
为什么该方法是私有的?当这些方法要从类中的多个地方调用时,我经常使用私有方法。如果这个方法只是从一个地方调用,最好内联调用它的方法。
在我看来,我会将您的代码介绍给单元测试 测试将暗示什么是坏的,什么是好的 这听起来很疯狂,但随着时间的推移,你会学会“听到测试对你的代码说什么”。
但首先它需要一些集成测试,一个安全网,它将确认我们的重构没有破坏代码。
首先,我们对所有异常情况和有效案例进行集成测试:
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);
}
}
在我看来,这是一种提高可维护性的方法。因为,如果将来你需要在图片网址中添加正则表达式检查,例如。您只需添加另一个验证器。
接下来就是申请inversion of control
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);
}
}
就是这样。您将看到集成测试继续传递,这是非常好的。
答案 1 :(得分:0)
没关系。
Symfony有类似的文件系统代码:https://github.com/symfony/Filesystem/blob/master/Filesystem.php:)