用于包装数据库行子类的 OOP 方法
假设我想将狗存储在数据库表中,每只狗在 PHP 中都有自己的子类。
基本上我想避免在代码中的不同位置存储/列出子类名称。对此,什么是好的 OOP 方法呢?
abstract class Dog {
protected $data;
public function __construct($data) {
$this->data = $data;
}
public function name() {
return $this->data["name"];
}
abstract public function breed();
}
class GermanShepherd extends Dog {
public function breed() {
return _("German Shepherd");
}
}
class BullDog extends Dog {
public function breed() {
return _("Bulldog");
}
}
现在我有这个类来处理对象组(即狗):
class Dogs {
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
switch ($row["type"]) { // I could do this using a lookup array
case "shepherd": $dog = "GermanShepherd"; break;
case "bulldog": $dog = "Bulldog"; break;
}
$ret[] = new $dog($row);
}
return $ret;
}
}
我想使用这个类来获取我视图中的狗类型(特别是对于添加狗表单),而不是列出类名:
?><form><select name="type"><?php
foreach (array("GermanShepherd", "Bulldog") as $dog) { // here I would like to do avoid listing the class names again
?><option value="<?=$dog ?>"><?php
$d = new $dog; // actually I can't instantiate the class here because I don't have any data at this point
echo $d->name();
?></option><?php
}
?></select></form><?php
我想将其合并到 Dogs
类中,大致如下:到目前为止,
class Dogs {
private static $dogs = array(
"shepherd" => "GermanShepherd",
"bulldog" => "Bulldog",
);
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
$dog = self::$dogs[$row["type"]];
$ret[] = new $dog($row);
}
return $ret;
}
public static function getDogTypes() {
return array_values(self::$dogs);
}
}
?><form><select name="type"><?php
foreach (Dogs::getDogTypes() as $dog) {
?><option value="<?=$dog ?>"><?php
$d = new $dog; // here I still need to instantiate the class and I don't have any data to provide it with
echo $d->name();
?></option><?php
}
?></select></form><?php
这会有些作用,但是如果我需要更多类特定信息,例如当我有更多特定于狗类型的字段吗?
foreach (Dogs::getDogTypes() as $dog) {
$d = new $dog; // instantiate again?
foreach ($d->formFields() as $f) { // I wouldn't do it like this, just putting this here for demonstrative purposes
echo $f;
}
}
我认为部分问题在于我需要能够在有或没有数据库数据的情况下使用我的类:当我从数据库表中获取数据时,一切看起来都非常合理,但是当我生成创建新狗时形成。
感谢您的想法!
Let's say I want to store dogs in a database table with each dog having its own subclass in PHP.
Basically I want to avoid storing/listing the subclass names in different places in the code. What would be a good OOP approach for that?
abstract class Dog {
protected $data;
public function __construct($data) {
$this->data = $data;
}
public function name() {
return $this->data["name"];
}
abstract public function breed();
}
class GermanShepherd extends Dog {
public function breed() {
return _("German Shepherd");
}
}
class BullDog extends Dog {
public function breed() {
return _("Bulldog");
}
}
Now I have this class that handles groups of objects (i.e. dogs):
class Dogs {
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
switch ($row["type"]) { // I could do this using a lookup array
case "shepherd": $dog = "GermanShepherd"; break;
case "bulldog": $dog = "Bulldog"; break;
}
$ret[] = new $dog($row);
}
return $ret;
}
}
And I would like to use this class to get the dog types in my view (especially for an add dog form), instead of listing the class names:
?><form><select name="type"><?php
foreach (array("GermanShepherd", "Bulldog") as $dog) { // here I would like to do avoid listing the class names again
?><option value="<?=$dog ?>"><?php
$d = new $dog; // actually I can't instantiate the class here because I don't have any data at this point
echo $d->name();
?></option><?php
}
?></select></form><?php
I would like to incorporate this into the Dogs
class, something along the lines of this:
class Dogs {
private static $dogs = array(
"shepherd" => "GermanShepherd",
"bulldog" => "Bulldog",
);
public static function getDogs() {
// ...
$ret = array();
while ($row = mysql_fetch_assoc()) {
$dog = self::$dogs[$row["type"]];
$ret[] = new $dog($row);
}
return $ret;
}
public static function getDogTypes() {
return array_values(self::$dogs);
}
}
?><form><select name="type"><?php
foreach (Dogs::getDogTypes() as $dog) {
?><option value="<?=$dog ?>"><?php
$d = new $dog; // here I still need to instantiate the class and I don't have any data to provide it with
echo $d->name();
?></option><?php
}
?></select></form><?php
This would somewhat work so far, but what if I need more class specific information, for example when I have more fields specific to a dog type?
foreach (Dogs::getDogTypes() as $dog) {
$d = new $dog; // instantiate again?
foreach ($d->formFields() as $f) { // I wouldn't do it like this, just putting this here for demonstrative purposes
echo $f;
}
}
I think part of the problem lies in the fact that I need to be able to use my classes with and without database data: Everything seems very reasonable when I have the data from the database table, but I also need the data when I generate the form when creating a new dog.
Thanks for your ideas!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先使用接口。这将向您表明,拥有更具体的接口(每个子类有不同的类方法和属性)将使您需要以不同的方式具体处理它们。因此,它们将向您展示缺陷所在,并使您能够将对象简化为更可重用的东西。
只要您的对象仅存储一些数据,请使用数据传输对象 - 这是任何“类型”。所以你不需要处理类型。但是,如果您想保持基本,您也可以使用
StdClass
或Array
来实现这一点。优点是:您不需要实际编写那么多代码。如果它还不够(将来也会如此),请仅在需要时添加代码。从长远来看应该让事情变得更简单。因此,从基本的数据传输对象类开始,并在其基础上进行构建。
因此,使用您编写的类来分离关注点,而不是交织关注点。封装变化的部分,以便您的代码实际上可以从您的设计中受益。
First make use of Interfaces. This will show you that having more specific interfaces (different class methods and properties per subclass) will make you need to deal with them differently in concrete. So they will show you where the deficiencies are and will enable you to streamline your objects into something more re-useable.
As long as your objects are only storing some data, use a data transfer object instead - which is of any "type". So you don't need to deal with type. However, you can use
StdClass
orArray
for that as well if you want to keep it basic. The plus-side is: You don't need to actually write that much code.In case it's not sufficient (as it will be), only add the code when you need to. Should keep things more simple in the long run then. So start with a basic data transfer object class and build upon it.
So use the classes you write to separate concerns, not to interweave the concerns. Encapsulate what varies, so your code can actually benefit from your design.
我认为 Dogs 类中的静态数组是一个完全可以接受的解决方案。它不包括实例化问题,但您可以使用(静态)工厂方法来修复该问题。为了使实例化更加容易和更具可扩展性,您可以确保存储的字符串以某种方式映射到对象名称:
我认为
->getFormFields()
方法根本不是一个坏主意;当每个 Dog 类型的字段不同时,将其合并到对象中将是完全有效的 OO!I think the static array in your Dogs class is quite an acceptable solution. It doesn't cover for the instantiating problem, but you can fix that with a (static) factory method. To make the instantiating even easier and more scalable, you can make sure the stored strings map to object names somehow:
I don't think a
->getFormFields()
method is a bad idea at all; when the fields differ per Dog type, it would be perfectly valid OO to incorporate that in the object!将你的狗存储在二维数组中怎么样?
What about storing your dogs in a 2D array?