我有一个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对象。不幸的是,如果业务逻辑将以某种方式出现在代码中,你需要对其进行硬编码。使用这种方法,你不会对每种类型进行硬编码,但你需要为每种类型创建一个策略类。在示例中,我们有StrategyCaps
、StrategyStars
和StrategyExclaim
策略。因此,我们的类型仅限于Caps
、Stars
和Exclaim
。
我没有在生产环境中尝试过这段代码,但您可以通过示例来获得起点。
对于动态加载,您也可以从这个问题中受益。在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语句。