基于对象类型的操作的PHP最佳实践


PHP best practice for action based on an object type

我有一个PHP/Laravel最佳实践问题。

以下是场景:我有一个包含"type"属性的对象。type属性将设置为某种整数。

根据对象中的"类型"整数,必须执行不同的操作。

处理此对象的最佳方式是什么?我拼命想办法避免使用一大堆if/else语句,因为这感觉是一种非常错误和丑陋的方法。

即我不想要这个:

if($obj->type == 1) {
    //process actions for type 1
}
else if($obj->type == 2){
    //process actions for type 2
}

如有任何建议,不胜感激。

谢谢!

感谢@Ryan Vincent我找到了这个资源(https://sourcemaking.com/design_patterns/strategy/php)并稍微改变了战略设计模式。为了避免硬编码的类型值,请检查动态类加载是如何在StrategyContext::__construct方法中完成的。新的类实例由$type变量名启动。PHP中的类名应该是strings,所以这种方式强制类型是strings,而不仅仅是数字。

与文章中的原始示例不同,我将StrategyContext附加到了book对象,并将get方法与策略包装在一起,以更好地利用book对象。不幸的是,如果业务逻辑将以某种方式出现在代码中,你需要对其进行硬编码。使用这种方法,你不会对每种类型进行硬编码,但你需要为每种类型创建一个策略类。在示例中,我们有StrategyCapsStrategyStarsStrategyExclaim策略。因此,我们的类型仅限于CapsStarsExclaim

我没有在生产环境中尝试过这段代码,但您可以通过示例来获得起点。

对于动态加载,您也可以从这个问题中受益。在PHP中从变量实例化一个类?

希望有帮助,

<?php
interface StrategyInterface {
    public function showTitle($title);
    public function showAuthor($author);
}
class StrategyContext  implements StrategyInterface {
    private $strategy = NULL; 
    public function __construct($type) {
        //Dynamic class loading per type
        $classname="Strategy{$type}";
        if(class_exists($classname)) {
          $this->strategy = new $classname();
        } else {
          throw new Exception("Strategy not found", 1);
        }
    }
    public function showTitle($title) {
      return $this->strategy->showTitle($title);
    }
    public function showAuthor($author) {
      return $this->strategy->showAuthor($author);
    }
}

class StrategyCaps implements StrategyInterface {
    public function showTitle($title) {
        return strtoupper ($title);
    }
    public function showAuthor($author) {
        return strtoupper ($author);
    }
}
class StrategyExclaim implements StrategyInterface {
    public function showTitle($title) {
        return Str_replace(' ','!',$title);
    }
    public function showAuthor($author) {
        return Str_replace(' ','!',$author);
    }
}
class StrategyStars implements StrategyInterface {
    public function showTitle($title) {
        return Str_replace(' ','*',$title);
    }    
    public function showAuthor($author) {
        return Str_replace(' ','*',$author);
    }
}
class Book {
    private $author;
    private $title;
    private $strategy;
    function __construct($strategy, $title_in, $author_in) {
        $this->strategy = new StrategyContext($strategy);
        $this->author = $author_in;
        $this->title  = $title_in;
    }
    function getAuthor() {
        return $this->strategy->showAuthor($this->author);
    }
    function getTitle() {
        return $this->strategy->showTitle($this->title);
    }
    function getAuthorAndTitle() {
        return $this->getTitle() . ' by ' . $this->getAuthor();
    }
}
writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');
$type = 'Caps';
$book = new Book($type, 'PHP for Cats','Zeev Suraski');
writeln($book->getAuthorAndTitle());

$type = 'Exclaim';
$book = new Book($type, 'PHP for Unicorns','Rasmus Lerdorf');
writeln($book->getAuthorAndTitle());

$type = 'Stars';
$book = new Book($type, 'PHP for Ponys','Andi Gutmans');
writeln($book->getAuthorAndTitle());

function writeln($line_in) {
  echo $line_in.PHP_EOL;
}

更新:

因此,如果您使用ORM,我们可以假设Book是您的模型类。我对Eloquent以及它如何处理数据绑定等一无所知,所以我会尽可能地简化它。所以我假设您可以使用一个构造函数来绑定数据库中的数据。

将你的StrategyContext和实际的策略类(你的业务逻辑将在其中编码(作为一项服务,并使用依赖注入,同时找出你将使用的策略。通过这种方式,您可以根据type变量将所有策略绑定到Model对象中。

图书类的更新版本

class Book {
    private $author = "Zeev Suraski";
    private $title = "PHP for Cats";
    private $strategy;
    private $type = 'Caps';
    function __construct() {
        $this->strategy = new StrategyContext($this->type); //Dependency injection here
    }
    function getAuthor() {
        return $this->strategy->showAuthor($this->author);
    }
    function getTitle() {
        return $this->strategy->showTitle($this->title);
    }
    function getAuthorAndTitle() {
        return $this->getTitle() . ' by ' . $this->getAuthor();
    }
    function setType($type) {
      $this->type = $type;
    }
    function setStrategy($type=null) {
      if($type==null) {
        $this->strategy = new StrategyContext($this->type); //Dependency injection here
      } else {
        $this->strategy = new StrategyContext($type); //Dependency injection here
        $this->setType($type);
      }
    }
}

writeln('BEGIN TESTING STRATEGY PATTERN');
writeln('');
$book = new Book();
writeln($book->getAuthorAndTitle());

$type = 'Exclaim';
$book->setType($type);
$book->setStrategy();
writeln($book->getAuthorAndTitle());

$type = 'Stars';
$book->setStrategy($type);
writeln($book->getAuthorAndTitle());
function writeln($line_in) {
  echo $line_in.PHP_EOL;
}

我会使用PHP的switch语句。例如,

switch($obj->type) {
    case 1:
        // do something
        break;
    case 2:
        // do something else
        break;
    default:
        // default actions
        break;
}

这里,break用于作为一个整体停止switch语句的执行。如果没有中断,代码执行可能会进入下一种情况。不过,这有时是令人向往的行为。如果您希望案例1和案例2运行相同的代码,可以将案例1留空,然后将所有代码都写在案例2中。这样,当满足情况1的条件时,将不会运行与情况1相关的代码,但如果没有break语句,代码执行将继续到情况2,然后在情况2中到达break语句。