我在程序上用PHP编程(这是一个单词吗?)大约五年了,并决定尝试OOP方法但遇到了一些概念/设计问题。假设您在程序中有一些模块,每个模块都可以列出,添加,编辑和删除实体。一个实体可以是..Dunno,用户,客户,产品等。
您如何设计类来操纵这些实体?
我想到了两种可能性:
提前致谢,
答案 0 :(得分:5)
我会创建一个interface来定义您的列表,添加,编辑和删除方法。这为您提供了一个类“模板”。如果您的类(用户,客户端,产品等)实现此接口,则必须在这些类中定义接口中的方法。
这将为您提供类似的“API”,以访问实现您的界面的每个类的所有功能。由于每个列出的对象都包含不同的数据,因此方法的细节会有所不同,因而是分开的,但界面将是相同的。
<强>除了:强>
您在方法列表中包含“列表”会让我感到有些担忧。这似乎意味着您将对象视为用户,客户端,产品等的集合,其中很可能是表示单个用户的用户类,一个表示<的客户端类< em>单个客户等
另一方面,“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
无论如何,这就是我处理事情的方式。
答案 1 :(得分: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的好处时。
注意:DbRecord
和DbTable
是任意名称 - 使用您喜欢的w / e名称。
答案 2 :(得分:1)
使用第一种方法,使用方法创建可重用对象。您只需编码一次就不浪费时间。
class User {
function __construct() { /* Constructor code */ }
function load($id) { ... }
function save() { ... }
function delete() { ... }
}
答案 3 :(得分:1)
你正在使用'普通类'(也称为基类,或抽象类,以防他们的行为需要在子类可以投入使用之前补充)。
OOP方法是将所有实体的所有行为放在基类中。
如果您使用类似于ActiveRecord的东西,那么您已经有了一个用于create-update-delete操作的通用(抽象)接口。使用它对您有利,并让您的基类仅在这些接口方法上运行。他们不需要知道他们正在更新产品或用户,他们只需要知道他们可以在实体上调用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中,这也可以确保数据的完整性。
希望这会有所帮助,比我想要的更多,但我认为你试图解决这个问题是值得称赞的,而不会在问题上抛出一个框架:)