我正在创建一个基本上销售广告“点”的网站。即,有人可以注册并购买要在主页上显示的横幅广告,或者他们可以在他们获得自己的个人资料页面的情况下购买广告。我的观点是,虽然所有广告都有共同的功能,但它们确实有所不同。
为实现这一目标,我的域模型如下所示:(简化)
class Advert {
protected
$uID,
$startTime,
$traits = array();
public function __construct($_traits) {
$this->traits = $_traits;
}
public function getUID() { return $this->startTime; }
public function getStartTime() { return $this->startTime; }
public function setStartTime($_startTime) { $this->startTime = $_startTime; }
public function save() {
MySQLQuery 'UPDATE adverts SET startTime = $this->startTime WHERE uID = $this->uID';
foreach($this->traits as $trait) {
$trait->save($this->uID);
}
}
....
}
-
interface IAdvertTrait {
public function save($_advertUID);
}
-
class AdvertTraitProfile implements IAdvertTrait {
protected $url;
public function getURL() { return $this->url; }
public function setURL($_url) { $this->url = $_url; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
}
....
}
-
class AdvertTraitImage implements IAdvertTrait {
protected $image;
public function getImage() { return $this->image; }
public function setImage($_image) { $this->image = $_image; }
public function save($_advertUID) {
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
}
....
}
实际上有几个'AdvertTrait ...'类,所有这些类都实现了IAdvertTrait。
如您所见,如果我创建这样的广告:
$advert = new Advert(
array(
new AdvertTraitProfile(),
new AdvertTraitImage()
...
)
);
我可以这样做:
$advert->save();
所有必需的信息将由广告本身及其每个广告传输保存到数据库。
使用这种方法我只需传递不同的“特征”就可以创建不同类型的广告。但是,对我的问题 - 我不知道如何操纵广告。根据上面的例子,创建和广告确实没有意义,然后立即保存它。
我希望能够做到这一点:
$advert->getStartTime(); # Works
$advert->getURL(); # Doesn't work of course, as the getURL method is encapsulated within a property of the Advert's 'traits' array
$advert->setImage('blah.jpg'); # Also does not work
我不确定如何使这些“内部”方法可以访问。
我可以为每种广告创建一个不同的“广告”类,即:
AdvertProfile extends Advert {
$this->traitProfile = new AdvertTraitProfile();
public function getURL() { return $this->traitProfile->getURL(); }
...
}
AdvertImage extends Advert {
$this->traitImage = new AdvertTraitImage();
public function getImage() { return $this->traitImage->getImage(); }
...
}
AdvertProfileImage extends Advert {
$this->traitProfile = new AdvertTraitProfile();
$this->traitImage = new AdvertTraitImage();
public function getURL() { return $this->traitProfile->getURL(); }
public function getImage() { return $this->traitImage->getImage(); }
...
}
但我觉得这会变得混乱;我需要继续为我需要的每个特征组合创建新的“广告”类,并且每个广告类都需要自己定义其特征方法,以便可以从广告实例中调用它们。
我也搞砸了装饰模式;所以我没有将这些'trait'类传递给Advert的构造函数,而是将装饰器链接在一起,如:
$ advert = new AdvertImageDecorator(new AdvertProfileDecorator(new Advert()));
然而,这需要装饰器能够使用method_exists和call_user_func_array“查找”不属于它们的方法,这对我来说似乎是一个很大的黑客。加上将众多装饰者链接在一起,就像只是在我身上一样。
我也看过正确的PHP Traits,但IMVHO我认为他们不会帮助我。例如,每个AdvertTrait都有一个“保存”方法,所有这些方法都需要同时调用。我相信一个合适的特质会要求我从一个特征中选择一个“保存”方法。
也许我应该使用普通的旧继承 - 但是我仍然会创建特定类型的广告,所有这些广告最终都继承自广告。但是我相信这会引起进一步的问题;即我无法从AdvertWithProfileTraits和AdvertWithImageTraits扩展AdvertWithProfileAndImageTraits。
任何人都可以为这个难题提供合适的解决方案吗?也许我应该使用另一种设计模式。
非常感谢, 戴夫
答案 0 :(得分:3)
我会选择装饰器方法
抽象AdvertDecorator
类可以如下所示:
abstract class AdvertDecorator implements IAdvertTrait {
protected $child;
public function __construct($child=null) {
if(!$child) {
$child = new NullAdvert();
}
$this->child = $child;
}
/**
* With this function all calls to non existing methods gets catched
* and called on the child
*/
public function __call($name, $args) {
return call_user_func_array(array($this->child, $name), $args);
}
}
/**
* This class is for convenience so that every decorator
* don't have to check if there is a child
*/
class NullAdvert implements IAdvertTrait {
public function save($_advertUID) {
// do nothing
}
}
您可以使用NullAdvert
类来代替BaseAdvert
类,该类实现所有基本广告逻辑(就像您在Advert
类中所做的那样)。 / em>的
现在所有其他类都从此AdvertDecorator
类扩展:
class AdvertProfile extends AdvertDecorator {
public function getProfileURL() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_profile SET url = $this->url WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertImage extends AdvertDecorator {
public function getImage() { ... }
public function save($_advertUID) {
// save own advert
MySQLQuery 'UPDATE advertdata_image SET image = $this->image WHERE advertUID = $_advertUID';
// save advert of child
$this->child->save($_advertUID);
}
}
class AdvertProfileImage extends AdvertDecorator {
public function getProfileImageURL() { ... }
public function getProfileImage() { ... }
public function save($_advertUID) {
// save own advert ...
// save advert of child
$this->child->save($_advertUID);
}
}
你可以像这样使用它:
$advert = new AdvertProfile();
$advert = new AdvertImage($advert);
$advert = new AdvertProfileImage($advert);
// save all advert components
$advert->save('uid');
// call functions
$advert->getProfileURL();
$advert->getImage();
$advert->getProfileImageURL();
$advert->getProfileImage();
这种结构是恕我直言非常灵活。每个广告组件都可以按任意顺序添加到当前广告中。此外,您可以使用复合模式扩展此解决方案并添加AdvertComposite
,以便您可以对组件进行分组。您甚至可以将同一类型的多个广告组件添加到一个广告(为此您必须稍微更改一些方法)。