2

我知道这方面有很多问题,我已经做了很多阅读。我想在我的项目背景下问这个问题,看看您可能有什么建议。

我有一个相当大的 Web 应用程序,其中包含许多类,例如用户文章(我认为它们是主要类)和较小的类,例如图像评论。现在在一个页面上,比如说一篇文章,它可能包含许多图像评论的实例。有道理吗?现在说一个文章页面,我调用一个返回文章对象数组的静态方法。

这就是背景,所以这里有问题。

由于构建了大量的应用程序,我开始意识到拥有一个包含设置和共享功能的核心系统类将非常有用。在那里,我用一个新的核心课程扩展了我的所有课程。似乎相对简单且快速实施。我知道 CodeIgniter 做了类似的事情。我现在感觉虽然我的应用程序变得有点混乱。

问题这是个好主意吗?创建核心实例正是我在调用文章实例时想要的,但是当我使用静态方法创建多个实例时,或者在页面上调用多个图像或评论时呢?我不必要地调用核心课程对吗?实际上它只需要每页调用一次(例如,构造函数从数据库中定义各种设置,我不想每次都这样做,显然每页只调用一次),但是所有类的所有实例都应该可以访问它核心班。听起来就像我想要单例方法一样,但我知道这在 PHP 中是浪费时间。

这是我的代码此时的样子我尽量保持简单。

class core {

    public function __construct(){
        ...define some settings which are retrieve from the database
    }

    public function usefulFunction(){
    }
}


class user extends core {

    public function __construct(){
        parent::__construct();
    }

    public function getUser($user_id){
        $db = new database();
        $user = /* Get user in assoc array from db */

        $this->__setAll($user);
    }

    public static function getUsers(){
        $db = new database();
        $users = /* Get users from database in assoc array from db */

        foreach($users as $user) {
             $arrUsers[] = new self();
             $arrUsers[]->__setAll($user);
        }

        return $arrUsers;
    }

    private function __setAll($attributes) {
        foreach($attributes as $key => $value)
        {
            $this->__set($key, $value);
        }   
    }

    public function __set($key, $value) {
         $this->$key = $value;
    }

}

我遇到的另一个问题是有效地使用/共享数据库连接。目前,需要数据库连接的类中的每个方法都会创建一个新的数据库实例,因此在一个页面上我可能会这样做 5 或 10 次。像依赖注入原理这样的东西听起来要好得多。

问题现在,如果我将数据库的实例传递给新的用户类,我知道我需要这样的东西......

class user{
    protected $db;

    public function __construct($db){
        $this->db = $db;
    }

    ... etc
}

$db = new database();
$user = new user($db);

...但是当我想运行静态函数 users::getUsers() 时,访问数据库实例的最佳方法是什么?我需要在每个静态方法中将它作为变量传递吗?(许多类中有许多静态方法)。这似乎不是最好的方法,但也许没有其他方法。

问题如果按照第 1 部分中的建议将我的所有类从核心类中扩展出来,我可以在那里创建一个数据库实例并以某种方式访问​​它吗?

问题我还有各种包含函数(不是 oop)的文件,它们就像帮助文件。这些访问数据库的最佳方式是什么?我再次在每个函数中创建了一个新实例。我真的不想将数据库作为参数传递给每个人。我应该使用全局变量,将这些帮助文件转换为类并使用依赖注入或其他不同的东西吗?

我知道那里有很多建议,但是大多数关于 PHP 的信息和教程都已经过时了,而且似乎从来没有涵盖过这么复杂的东西……如果你可以称之为复杂的话?

关于如何最好地布局我的班级结构的任何建议。我知道这看起来很多,但肯定这是大多数开发人员每天都面临的问题。如果您需要更多信息,请告诉我并感谢您的阅读!

4

4 回答 4

1

您可以从处理所有数据库查询的抽象类开始,然后将它们构造成对象。通过这种方式设置参数化查询将很容易,并且它将标准化您与数据库的交互方式。它还将使添加新对象模型变得轻而易举。

http://php.net/manual/en/language.oop5.abstract.php

abstract class DB
{
  abstract protected function table();
  abstract protected function fields();
  abstract protected function keys();

  public function find()
  {
    //maybe write yourself a parameterized method that all objects will use...
    global $db; //this would be the database connection that you set up elsewhere.
    //query, and then pack up as an object
  }

  public function save()
  {
  }

  public function destroy()
  {
  }
}

class User extends DB
{
  protected function table()
  {
    //table name
  }  

  protected function fields()
  {
    //table fields here
  }

  protected function keys()
  {
    //table key(s) here
  }

  //reusable pattern for parameterized queries
  public static function get_user( $id )
  {
    $factory = new User;
    $params = array( '=' => array( 'id' => $id ) );
    $query = $factory->find( $params );
    //return the object
  }
}

您将希望从一个通用配置文件进行数据库连接,并将其作为此模式的全局变量。

显然这只是表面上的,但希望它能给你一些想法。

于 2012-07-28T19:33:03.323 回答
1

你在评论中问我应该详细说明为什么这是一个坏主意。我想强调以下内容来回答这个问题:

问问自己是否真的需要它。

根据需要做出设计决策,而不仅仅是因为你做到。在您的情况下,问问自己是否需要核心课程。正如你已经在评论中被问到你写的你实际上并不需要它,所以答案很清楚:这样做是不好的,因为它不是必需的,并且不需要它会引入很多副作用。

由于这些副作用,您不想这样做。那么从零到英雄,我们来做如下进化:

您有两部分代码/功能。一部分确实发生了变化,另一部分是一些基本功能(框架、库)不会改变。您现在需要将它们放在一起。让我们将其简化并将框架简化为单个函数:

function usefulFunction($with, $four, $useful, $parameters)
{
    ...
}

让我们将应用程序的第二部分——改变的部分——简化为单个User类:

class User extends DatabaseObject
{
    ...
}

我已经在这里介绍了一个小而重要的更改:User该类不再扩展自,Core而是从自扩展,DatabaseObject因为如果我正确阅读了您的代码,它的功能是表示数据库表中的一行,可能即用户表。

我已经做出了这个改变,因为有一个非常重要的规则。每当您在代码中命名某些东西时,例如一个类,请使用一个会说话的好名字。名字就是给某物命名。这个名字Core绝对没有说明你认为它是重要的、一般的、基本的或内心深处的,或者它是铁水。没有线索。因此,即使您为设计命名,也要选择一个好名称。我想,DatabaseObject这只是一个非常快速的决定,甚至不知道你的代码,所以我很确定你知道那个类的真实名称,而且你也有责任给它真实的名字。它值得一个,是慷慨的。

但是让我们把这个细节放在一边,因为它只是一个细节,与你想要解决的一般问题没有太大关系。假设坏名声是一种症状,而不是原因。我们现在玩 Dr. House 并对症状进行分类,但只是为了找出原因。

目前发现的症状:

  • 多余的代码(即使不需要也写一个类)
  • 不好的命名

我们可以诊断:迷失方向?:)

因此,要摆脱这种情况,请始终做需要做的事情并选择简单的工具来编写代码。例如,提供常用功能(您的框架)的最简单方法就是使用以下include命令:

 include 'my-framework.php';    
 usefuleFunction('this', 'time', 'really', 'useful');

这个非常简单的拖线脚本演示了:应用程序中的一部分负责提供所需的功能(也称为加载),而另一部分正在使用这些功能(这只是我们从一开始就知道的程序代码,对?)。

这个映射/缩放到用户对象可能扩展的更多面向对象的示例如何?完全相同的:

include 'my-framework.php';
$user = $services->store->findUserByID($_GET['id']);

这里的区别只是里面my-framework.php多了个加载,让常用变化的部分可以利用不变化的东西。例如,可以提供一个表示服务定位器的全局变量(此处$services)或提供自动加载。

你越简单地保持这一点,你就会进步得越好,最后你将面临真正的决定。通过这些决定,您将更直接地看到有什么不同。

如果您想对“数据库类”进行更多讨论/指导,请考虑阅读《企业应用程序架构模式》一书中关于如何处理这些问题的不同方法的非常好的一章,这本书的标题有点长,但它有一章很好地讨论了该主题,并允许您选择一个合适的模式来轻松访问您的数据库。如果你从一开始就让事情变得简单,那么你不仅进步得更快,而且以后更容易改变它们。

但是,如果您从一些从基类扩展的复杂系统开始(甚至可能一次做多项事情),事情从一开始就不会那么容易改变,这将使您坚持这样的决定更长时间。到那时。

于 2012-07-29T11:46:34.510 回答
1

总结所有答案:

  1. 不要使用单一的“上帝”类作为核心。
  2. 最好使用能够完成工作的类列表。根据需要创建尽可能多的课程。每个班级应负责一项工作。
  3. 不要使用单例,这是旧技术,不灵活,请改用依赖注入容器(DIC)
于 2012-07-29T12:12:13.303 回答
0

首先,最好的办法是使用单例模式来获取数据库实例。

class Db{
protected $_db;

private function __construct() {
       $this->_db = new Database();
}

public static function getInstance() {
if (!isset(self::$_db)) {
    self::$_db = new self();
}
return self::$_db;
}
}

现在您可以像db::getInstance();在任何地方一样使用它。

其次,您正在尝试在功能上发明称为 Active Record 模式的自行车__setAll($attributes).

第三,你为什么在扩展Core的类里写这个东西?

public function __construct(){
    parent::__construct();
}

最后,类名应该大写。

于 2012-07-28T13:49:48.477 回答