使用扩展类时的最佳技巧(PHP)

发布于 2024-10-14 21:20:21 字数 513 浏览 2 评论 0原文

关于我在这里的帖子:扩展 PHP 类时的问题

最好的解决方案是什么?为什么?


$query = $this->Execute("SELECT * FROM $table");
$total = $this->NumRows($query);


$query = DBManager::Execute("SELECT * FROM $table");
$total = DBManager::NumRows($query);


$query = parent::Execute("SELECT * FROM $table");
$total = parent::NumRows($query);

Regarding my post here: problems when extending a PHP Class

What's the best solution? And why?


$query = $this->Execute("SELECT * FROM $table");
$total = $this->NumRows($query);

Or


$query = DBManager::Execute("SELECT * FROM $table");
$total = DBManager::NumRows($query);

Or


$query = parent::Execute("SELECT * FROM $table");
$total = parent::NumRows($query);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

拒绝两难 2024-10-21 21:20:21

当您扩展一个类时,除了子类定义的任何方法和属性之外,子类还可以调用父类的所有公共和受保护的方法和属性。如果您不需要重写父类的方法,而只是想在子类中的某个地方使用它们,请使用

public function somethingUsingExecute()
{    
    $query = $this->Execute("SELECT * FROM $table");
    …
}

如果您需要在子类中重写execute,但又想使用原始方法调用该方法父级的功能, use

public function execute($table)
{
    // can do something not in parent before parent call
    $query = parent::Execute("SELECT * FROM $table");
    // can do something not in parent after parent call
}

静态调用

$query = DBManager::Execute("SELECT * FROM $table");

是您应该忘记它存在的东西,因为您将类名硬编码到 using 类中,并将方法调用耦合到全局范围。两者都很难测试和维护。我不会详细介绍,因为之前已经讨论过这个问题。只需搜索一下即可。


关于该 User/DBManager 示例的“最佳技巧”实际上是根本不使用继承。继承创建了一种is-a关系。孩子是父母的特例。但用户不是 DBManager(顺便说一句,我将其称为 DbAdapter)。

DBManager 类的职责是允许访问数据库。您的用户没有该责任。它使用 DBManager 来访问数据库,但它确实需要知道如何详细执行该操作。 DBManager 已经这样做了。

任何使用另一个类的类都应该被注入/聚合。因此,您应该做的是:

class User …

    protected $db;
    protected $isAuthenticated = FALSE;

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

    public function authenticate($username, $password)
    {
        if($this->db->query('SELECT … ')) {
            $this->isAuthenticated = TRUE;
        }
        return $this->isAuthenticated;
    }

    …
}

然后您可以执行

$db = new DBManager;
$john = new User($db);
$john->authenticate('JohnDoe', 'jd/12345');
$jane = new User($db);
$jane->authenticate('JaneDoe', 'jd/67890');
$logger = new DBLogger($db);
$logger->log('something to db');

注入 DB 类的额外好处,即您可以在其他使用类中重用现有的 DBManager 实例。如果扩展 DBManager 类,每个新实例都会创建一个到数据库的新连接,出于性能原因,这是不希望的。

When you extend a class, the child class can call all the public and protected methods and properties of the parent class in addition to any the child defines. If you do not need to override the parent's methods, but just want to use them somewhere inside the child, use

public function somethingUsingExecute()
{    
    $query = $this->Execute("SELECT * FROM $table");
    …
}

If you need to override execute in a child class but also want to call that method with the original functionality of the parent, use

public function execute($table)
{
    // can do something not in parent before parent call
    $query = parent::Execute("SELECT * FROM $table");
    // can do something not in parent after parent call
}

The static call

$query = DBManager::Execute("SELECT * FROM $table");

is something you should forget it exists, because you are hardcoding a classname into the using class and also couple the method call to the global scope. Both is hard to test and maintain. I won't go into more details, because this has been discussed on SO before. Just give it a search.


The "best trick" regarding that User/DBManager example would actually be not use inheritance at all. Inheritance creates an is-a relationship. The child is a special case of the parent. But a User is not a DBManager (I'd call it DbAdapter btw).

The responsibility of your DBManager class is to allow access to the database. Your User does not have that responsibility. It uses the DBManager to get access to the database, but it does need to know how to do that in detail. The DBManager already does.

Any class using another class should be injected/aggregated. So, what you should do is:

class User …

    protected $db;
    protected $isAuthenticated = FALSE;

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

    public function authenticate($username, $password)
    {
        if($this->db->query('SELECT … ')) {
            $this->isAuthenticated = TRUE;
        }
        return $this->isAuthenticated;
    }

    …
}

Then you can do

$db = new DBManager;
$john = new User($db);
$john->authenticate('JohnDoe', 'jd/12345');
$jane = new User($db);
$jane->authenticate('JaneDoe', 'jd/67890');
$logger = new DBLogger($db);
$logger->log('something to db');

Injecting the DB class has the added benefit that you can reuse the existing DBManager instance in other using classes. If you extend the DBManager class, each new instance will create a new connection to the database, which is undesired for performance reasons.

记忆里有你的影子 2024-10-21 21:20:21

静态调用函数(选项 2 和 3)和作为非静态函数调用它们之间存在差异。这取决于您的整个设置

There is a difference between statically calling the functions (option 2 and 3) and calling them as non-static functions. It depends on your whole set-up

执笔绘流年 2024-10-21 21:20:21
  • $this 指向该类的当前实例
  • DBManager指向父类,但是当然,如​​果你改变它的名字,你就必须改变你的代码。
  • parent 指向该类继承的类,无论其名称如何。

因此,在这个示例中,如果没有有关如何使用类和实例的更多详细信息,我认为这并不重要。但是,如果您计划拥有 DB 类的多个实例,您可能应该使用 $this,至少如果您想存储查询或结果而不用下一个查询覆盖它们(如劳特姆建议)。

  • $this points to the current instance of the class.
  • DBManager points to the parent class, but of course, if you change its name, you have to change your code.
  • parent points to the class from which this one inherits, no matter its name.

So, in this example I don't think it matters much, without further details on how you would use your classes and instances. But, if for example you plan to have multiple instances of the DB class, you probably should use $this, at least if you want to store the queries or results without overwrite them with your next query (as raultm suggested).

逆夏时光 2024-10-21 21:20:21

如果我们仅限于您的选项(否则请阅读下面的第 3 项),我会选择第二个选项,因为:

1)当您编写

$query = $this->Execute("SELECT * FROM $table");
$total = $this->NumRows($query);

它时,就像您有一个类,该类执行查询(所有类型)并处理结果一些特定的查询(在您的情况下是行数。假设您有两个实体,它们都存储在表中(不同),您将有两个类,并且它们都将重复 Execute 方法逻辑,或者一个实体 DAO 将使用另一个实体来执行其 Execute 方法(但这些实体可以并且大多数情况下是不相关的,因此您将损害 OOP 原则)

2)现在您的最后一种情况

$query = parent::Execute("SELECT * FROM $table");
$total = parent::NumRows($query);

比前一种情况更好,但在这种情况下您 DAO继承自一个执行查询执行的类...我不确定这是正确的方法,假设您有两个不同的存储(例如数据库和 xml),用于从您将使用 SQL 的第一个存储中检索信息,例如最后一个可能是 XPath;要使用存储,您将使用相同的接口和两个实现,而不是问题 - 为您的 User 类继承哪一个?没有人,他们没有血缘关系;用户实体不应该知道它存储在哪里。

3)最后一种情况

$query = DBManager::Execute("SELECT * FROM $table");
$total = DBManager::NumRows($query);

听起来像是委托(抱歉,我对PHP不太熟悉),在这里你指定使用哪个类来处理查询,对我来说,当我读到这段代码时,听起来 Execute/NumRows 是静态方法DBManager 类(再次抱歉,我只是使用我的体系结构知识来帮助您和 Java 等基本约定来阅读您的代码)。在这种情况下,我建议您创建 DBManager 实例,然后使用它:

class UserDAO {  
   private $storageManager = new DBManager();

   ...  
   $query = $storageManager::Execute("SELECT * FROM $table");  
   $total = $storageManager::NumRows($query);  
   ....  
}

类似这样的内容(如果我在 PHP 代码中犯了错误,请纠正我;现在只需将其视为“要走的路”,抽象算法描述; )。在这种情况下,如果必须切换到 DBManager 的另一个实现,您只需更改行 private $storageManager = new DBManager(); 即可。

I would go with the second option if we are limited to your options (otherwise read item 3 below), because:

1) when you write

$query = $this->Execute("SELECT * FROM $table");
$total = $this->NumRows($query);

it seams like you have one class, which executes queries (all kind of), and processes result of some specific queries (in your case number of rows. Imagine you have two entities, both of them are stored in tables (different), you will have two classes and both of them will either duplicate Execute method logic, or one entities DAO will use another to execute its Execute method (but these entities can be and mostly will be not related, so you will harm OOP principles)

2) now your last case

$query = parent::Execute("SELECT * FROM $table");
$total = parent::NumRows($query);

better than previous one, but in this case you DAOs are inherited from a class, which performs query execution... I'm not sure that it is right way to go, Imagine you have two different storages, like database and xml, to retrive information from first one you will use SQL, for last one it will be probably XPath; to work with storage you will use the same interface and two implementations, not the question - which one to inherit for your User class? No one, they are not related; User entity should not have any idea where is it stored.

3) Last case

$query = DBManager::Execute("SELECT * FROM $table");
$total = DBManager::NumRows($query);

it sound like delegating (sorry, I'm not that familiar with PHP), here you specify which class to use to process queries, for me, when I read this code, it sounds like Execute/NumRows are static methods of DBManager class (again sorry, I just use my architecture knowledge to help you and basic conventions for e.g. Java to read your code). In this case I would recommend you to create instance of DBManager and then use it:

class UserDAO {  
   private $storageManager = new DBManager();

   ...  
   $query = $storageManager::Execute("SELECT * FROM $table");  
   $total = $storageManager::NumRows($query);  
   ....  
}

something like this (please correct me if I made mistake in PHP code; for now just look at it like 'the way to go', abstract algorithm description ;). In this case, if have to switch to another implementation of DBManager, you will just change line private $storageManager = new DBManager(); and that's all.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文