PHP 中的 OOP 方法

Posted

技术标签:

【中文标题】PHP 中的 OOP 方法【英文标题】:OOP approach in PHP 【发布时间】:2011-06-15 21:03:05 【问题描述】:

我在 php 程序上编程(这甚至是一个词吗?)现在大约五年,并决定尝试 OOP 方法,但遇到了一些概念/设计问题。假设您在程序中有一些模块,每个模块都可以列出、添加、编辑和删除实体。实体可以是..dunno、用户、客户、产品等。

您将如何设计类来操作这些实体?

我想到了两种可能性:

使用 getUsersList、addUser、editUser、delUser 等方法为每个实体创建类 这种方法似乎很耗费资源,因为在列表脚本中您只需要 getUsersList 和可能的 delUser 方法,而在添加用户弹出脚本中,您只需要 addUser 方法,而在编辑用户弹出脚本中只需要 editUser 方法。所以,你必须实例化一个对象,并且只使用它的两个或一个方法...... 创建通用类:列出、添加、编辑和删除并为每个实体扩展它们,这样您一次只需实例化一个类(您真正需要的类)

提前致谢,

【问题讨论】:

(相关) Learning PHP Class 【参考方案1】:

“通用类”(也称为基类或抽象类,以防它们的行为需要由子类补充才能投入使用)。

OOP 方法是将所有实体共有的所有行为放在基类中。

如果你使用类似于 ActiveRecord 的东西,你已经有了一个用于创建-更新-删除操作的通用(抽象)接口。使用它对您有利,并让您的基类仅在这些接口方法上运行。他们不需要知道他们正在更新产品或用户,他们只需要知道他们可以在实体上调用 update() 方法。

但即使不使用 AR 框架之类的功能非常丰富的东西(如果您对非常灵活的 ORM 感兴趣,请查看 Doctrine..),您也可以使用接口来抽象行为。

让我给你一个更详细的例子......

/** * Interface for all entities to use */ interface Entity static function newEntity(); static function fetch($id); function save(); function setProperties(array $properties); function delete(); /** * A concrete product entity which implements the interface */ class Product implements Entity public $productId; public $name; public $price; public $description; /** * Factory method to create a new Product * * @param integer $id Optional, if you have auto-increment keys you don't need to set it * @return Product */ public static function newEntity($id=NULL) $product = new Product(); $product->productId = $id; return $product; /** * Factory method to fetch an existing entity from the database * * @param integer $id * @return Product */ public static function fetch($id) // make select with supplied id // let $row be resultset if (!$row) return NULL; // you might devise different strategies for handling not-found cases; in this case you need to check if fetch returned NULL $product = new Product(); $product->productId = $id; $product->name = $row['name']; $product->price = $row['price']; $product->description = $row['description']; return $product; /** * Update properties from a propreties array * @param array $properties * @return void */ public function setProperties(array $properties) $this->name = $properties['name']; $this->price = $properties['price']; $this->description = $properties['description']; public function save() // save current product properties to database public function delete() // delete product with $this->productId from database /** * An abstract CRUD controller for entities */ abstract class EntityCrudController protected $entityClass = 'UNDEFINED'; // Override this property in child controllers to define the entity class name protected $editTemplate = NULL; // Override this to set an edit template for the specific entity protected $templateEngine; // Pseudo-Templating engine for this example /** * Display the edit form for this entity * @param integer $entityId * @return string */ public function editAction($entityId) // Fetch entity - this is not the most clean way to fetch, you should probably consider building a factory that encapsulates this. $entity = call_user_func($this->entityClass, 'fetch', $entityId); // Assign entity to your edit template, in this example I'm assuming we're using a template engine similar to Smarty // You can generate the html output in any other way you might like to use. $this->templateEngine->setTemplate($this->editTemplate); $this->templateEngine->assign('entity', $entity); return $this->template->render(); /** * Update an existing entity * * @param integer $entityId * @param array $postArray * @return string */ public function updateAction($entityId, array $formArray) // Be sure to validate form data first here, if there are errors call $this->editAction() instead and be sure to set some error information $entity = call_user_func($this->entityClass, 'fetch', $entityId); $entity->setProperties($formArray); $entity->save(); // Again, using our imaginary templating engine to display... $this->templateEngine->setTemplate($this->editTemplate); $this->templateEngine->assign('entity', $entity); $this->templateEngine->assign('message', 'Saved successfully!'); return $this->template->render(); // Devise similar generic methods for newAction/insertAction here /** * Concrete controller class for products * This controller doesn't do much more than extend the abstract controller and override the 2 relevant properties. */ class ProductCrudController extends EntityCrudController protected $entityClass = 'Product'; protected $editTemplate = 'editProduct.tpl'; // Usage example: // Display edit form: $controller = new ProductCrudController(); $htmlOutput = $controller->editAction(1); // Save product: $htmlOutput = $controller->updateAction(1, array('name' => 'Test Product', 'price' => '9.99', 'description' => 'This is a test product'));

当然,还有很多需要改进的地方......例如您通常不希望每次在实体上调用 fetch() 时都进行查询,而是只查询一次并将结果对象存储在 IdentityMap 中,这也可以确保数据完整性。

希望这会有所帮助,比我预期的要多一点,但我认为你尝试解决这个问题而不抛出一个框架来解决这个问题是值得称赞的 :)

【讨论】:

【参考方案2】:

您需要创建一个可靠的架构和框架来管理您的数据模型。这并不容易,而且随着数据模型的增长只会变得更加复杂。我强烈推荐使用 PHP 框架(Symfony、CakePHP 等),或者至少使用 ORM(Doctrine、Propel 等)。

如果你还想自己动手,我会从类似于下面的架构开始。

您将需要一个DbRecord 类,用于单独的记录操作(如保存、删除等)。这个DbRecord 类将被特定实体扩展,并为基本实体操作提供基础。

class DbRecord 
    public function save() 
        // save logic (create or update)
    

    public function delete() 
        // delete logic
    

    // other record methods


class User extends DbRecord 
    private $name;
    private $email;

    public function setName($name_) 
        $this->name = $name_;
    

    public function setEmail($email_) 
        $this->email = $email_;
    

从中,您可以执行单独的记录操作:

$user = new User();
$user->setName('jim');
$user->setEmail('jim@domain.com');

$user->save();

您现在需要一个 DbTable 类,用于对表进行批量操作(例如读取所有实体等)。

class DbTable 
    public function readAll() 
        // read all
    

    public function deleteAll() 
        // delete all logic
    

    public function fetch($sql) 
        // fetch logic
    

    // other table methods


class UserTable extends DbTable 
    public function validateAllUsers() 
        // validation logic
    

    // ...

从中,您可以执行批量/表格操作:

$userTable = new UserTable();
$users = $userTable->readAll();

foreach ($users as $user) 
    // etc

代码架构是网站正确扩展的关键。将数据模型划分为适当的类和层次结构非常重要。

同样,随着您网站的发展,手动管理数据模型会变得非常复杂。那时您将真正看到 PHP 框架或 ORM 的好处。

注意:DbRecordDbTable 是任意名称 - 使用您喜欢的名称。

【讨论】:

【参考方案3】:

我会创建一个interface 来定义您的列表、添加、编辑和删除方法。这为您提供了一个类“模板”。如果您的类(User、Client、Product 等)实现此接口,则接口中的方法必须在这些类中定义。

这将为您提供一个类似的“API”来访问实现您的接口的每个类的所有功能。由于您列出的每个对象都包含不同的数据,因此方法的详细信息会有所不同,因此是分开的,但接口将是相同的。

旁白:

您在方法列表中包含“列表”让我有点担心。这似乎暗示您将您的对象视为用户、客户、产品等的集合,其中很可能应该有一个代表单个用户的用户类,一个代表单客户端等

另一方面,“list”可以作为静态方法处理——一种可以在没有类实例的情况下调用的方法。

$bob = new User('bob');
$bob->add(); // Adds bob to the database
$fred = new User('fred');
$fred->add(); // Adds fred to the database

$users = User::list(); // Gives an array of all the users in the database

反正我就是这样处理事情的。

【讨论】:

User 和 Users 应该是单独的实体,正如您可能想要做的 User->delete(),但您可能还希望 Users->delete() 删除多个用户......正如您所说,Users 应该是一个集合,应该是可迭代的,并且应该有 length() 属性..而不是单个 User 取决于您希望User->addClient->addProduct->add() 的外观,它可能不是您接口的一部分,因为类必须使用 实现接口方法准确的方法签名。【参考方案4】:

使用您的第一种方法,在该方法中使用方法创建可重用对象。这不是浪费时间,因为您只需编写一次代码。

class User 
    function __construct()  /* Constructor code */ 
    function load($id)  ... 
    function save()  ... 
    function delete()  ... 

【讨论】:

是的,但这不是浪费资源吗?当您只需要一半或更少时,实例化整个对象并将其保存在内存中?尝试将其缩放到更大的范围.. @Catalin:默认情况下,OOP 比程序代码更庞大。看看 ORM,它们的效率非常低。然而,它加快了开发速度并使代码更简洁。 是的,是这样想的,但是考虑一下...您可以为函数创建一个命名规则并按程序工作..创建一个 user.utils.php 与 user_load();user_save( ); user_delete(); @Catalin:当然可以,但是他们不会在同一个对象上进行交互。您必须在函数之间传递第二个对象。

以上是关于PHP 中的 OOP 方法的主要内容,如果未能解决你的问题,请参考以下文章

PHP中的面向对象OOP中的魔术方法

PHP中的面向对象OOP中的魔术方法

PHP中的OOP

PHP5中的高级OOP?

PHP中的OOP

如何更改数组 PHP OOP 中的序列