使用PHP在多个函数中重复代码

时间:2015-07-06 16:37:25

标签: php design-patterns

我在php类中添加了一些代码,它有几个函数,重复代码我想重构。功能如下所示:

from lxml import html

tree = html.fromstring("""<html>
    <head>
        <link rel="stylesheet">
    </head>
    <body>
<b>Old car</b><br>
<sup>13</sup>CO <font color="red">v = 0</font><br>
ID: 02910<br>
<p>
<p><b>CDS</b></p>""")

print tree.xpath("//b/text()")[0] # "Old cars"

我现在需要添加一个完全相同但新代码的新函数。

以下是我想到的事情:

  • 使用回调函数作为参数,不知道如何。
  • 使用带有公共代码的单个函数,并返回一个布尔值。如果为true,则在每个函数中执行唯一代码。这对我来说似乎不优雅。
  • 为唯一代码使用抽象类和模板抽象方法,但我不确定如何传递$ differentObject。
  • 我甚至想知道这是否是Decorator模式问题,但我从未使用过这种方式。

这是什么样的正确模式?

更新:

以下是具有实际代码的两个功能。它们是Symfony2表单处理程序,用于为对象添加上传的图像

public function similarName($arg1, $arg2, $differentObject) 
{
    // $arg1 and $arg2 are common to all functions
    if ($firstCheck) {
        // some code
            if($secondCheck) {
                // some code
                if ($thirdCheck) {
                    // unique code with $differentObject
                }
            }
        }
    // return statement
}

3 个答案:

答案 0 :(得分:1)

答案可能是将“唯一代码”移动到不同的对象类,如果它仅取决于对象类型。

如果它取决于对象类型以外的因素,那么确实一个好的方法是“回调”

private function commonFunction($arg1, $arg2, $differentObject, $uniqueCallback) 
{
    // $arg1 and $arg2 are common to all functions
    if ($firstCheck) {
        // some code
            if($secondCheck) {
                // some code
                if ($thirdCheck) {
                    $uniqueCallback($differentObject);
                }
            }
        }
    // return statement
}

public function similarFunction($arg1, $arg2, $differentObject)
{
    $this->commonFunction($arg1, $arg2, $differentObject, 
        function($differentObject) {
            // uniqueCode
        });
}

答案 1 :(得分:1)

如果不确切知道您的数据代表什么,很难建议您。

如果所有函数的args相同,并且在uniqueCode操作之前对它们执行某些操作,那么创建一个将args作为参数并将检查作为方法的对象会更好?

换句话说,uniquecode似乎是你职能的核心。保持可见和可读,并重构验证部分。

类似的东西:

class ValidateArgs {

    private $arg1;
    private $arg2;

    public function __construct($arg1, $arg2) {
        $this->arg1 = $arg1;
        $this->arg2 = $arg2;
    }

    public function check() {

        //Check
        $check = $this->arg1 && $this->arg2;

        return $check;
    }
}

然后你会这样称呼:

public function similarName($arg1, $arg2, $differentObject) 
{
    $validation = new ValidateArgs($arg1, $arg2);
    if($validation->check()) {

        // unique code goes here

        return $result_of_unique_code;
    }
}

更新:

看到你的代码示例后,我相信你需要多次迭代才能成功重构它。慢慢地一步一步,试着让代码在每一步都更清洁。

以下是一些建议:

简化if / else结构。有时您可以完全避免使用else部分。例如,您可以检查$request->getMethod()并立即返回:

if ($request->getMethod() != 'POST') {
    return false;
}

//Rest of the code

count($errorList)验证似乎仅依赖于data['file']。我想你可以创建一个具有所有逻辑的函数。像这样:

public function constrainValidations($data) {
    $imageConstraint = new  \Symfony\Component\Validator\Constraints\Image();
    $imageConstraint->maxSizeMessage = Image::ERROR_MESSAGE;
    $imageConstraint->maxSize = Image::MAX_SIZE;
    $errorList = $this->validator->validateValue($data['file'], $imageConstraint);
    if (count($errorList) == 0) {
        return true;
    } else {
        return false;
    }
}

然后你的原始代码开始变得更加干净和可读:

if ($request->getMethod() != 'POST') {
    return false;
}

if (!$this->constrainValidations($data)) {
    return false;
}
//Unique code goes here
return true;

继续一步一步地做。试图取消所有if语句。 此外,这将使您可以更改return语句并开始抛出异常。

然后你可以开始考虑对象方法以获得额外的可读性。

我个人会避免任何涉及回调的解决方案,但这只是一种品味问题。

答案 2 :(得分:1)

好问题。

基于这两个例子,似乎Post和Event类都应该实现一种“ImageSource”接口。如果其他情况类似,并且还假设可以轻松更改Event,Post和其他类,我认为代码应该是这样的。我们调用常用函数“handleImageSource”:

public function handleImageSource(FormInterface $form, Request $request, ImageSource $imgsrc)
{
    if ($request->getMethod() == 'POST') {
        $form->bind($request);
        $data = $form->getData();
        $file = $data['file'];
        if ($data['file']) {
            $imageConstraint = new \Symfony\Component\Validator\Constraints\Image();
            $imageConstraint->maxSizeMessage = Image::ERROR_MESSAGE;
            $imageConstraint->maxSize = Image::MAX_SIZE;
            $errorList = $this->validator->validateValue($file, $imageConstraint);
            if (count($errorList) == 0) {
                $image = $imgsrc->createImageFromFile($file, $this->imageManager);
            } else {
                return false;
            }
            return true;
        } else {
            return true;
        }
    }

    return false;
}

然后,实现ImageSource接口的每个类都应该有这样的方法。例如,在Event类中:

public function createImageFromFile($file, $imageManager)
{
    if (!($image = $this->getImage()) ) {
        $image = new ImageEvent();//|| new ImagePost() || etc...
        $image->setEvent($this);//|| setPost() || etc...
        $image->setFile($file);
        $imageManager->saveImage($image);
    } else {
        $image->setFile($file);
    }
    $imageManager->createImage($image);
    return $image;
}

在其他情况下,或者如果您只想使用“handleToXxxx”方法重构类,我会在每次调用之前使用不同的代码创建一个匿名函数。例如,再次使用Event类:

$image_source = function($file, $imageManager) use ($Event){
    if (!($image = $Event->getImage()) ) {
        $image = new ImageEvent();//|| new ImagePost() || etc...
        $image->setEvent($this);//|| setPost() || etc...
        $image->setFile($file);
        $imageManager->saveImage($image);
    } else {
        $image->setFile($file);
    }
    $imageManager->createImage($image);
    return $image;
};
//Then call to the function
$theHandleObj->handleImageSource($form, $request, $image_source);

然后,在“$ theHandleObj”类中:

public function handleImageSource(FormInterface $form, Request $request, callable $imgsrc)
{
    if ($request->getMethod() == 'POST') {
        $form->bind($request);
        $data = $form->getData();
        $file = $data['file'];
        if ($data['file']) {
            $imageConstraint = new \Symfony\Component\Validator\Constraints\Image();
            $imageConstraint->maxSizeMessage = Image::ERROR_MESSAGE;
            $imageConstraint->maxSize = Image::MAX_SIZE;
            $errorList = $this->validator->validateValue($file, $imageConstraint);
            if (count($errorList) == 0) {
                $image = $imgsrc($file, $this->imageManager);
            } else {
                return false;
            }
            return true;
        } else {
            return true;
        }
    }

    return false;
}

希望这会有所帮助:)