继承类是一种好的编程实践吗?

6
我在我的数据库类中有一个PDO连接,最近我将其用作其他类的扩展,例如class Users extends Database,这使我始终保持着数据库连接,而不必在我的用户类中拥有一个函数。
然而,有人指出这样做是不好的实践,为什么这样做是不好的实践呢?如何在用户类中连接到我的数据库类而不需要扩展?
目前,我在我的viewall()函数中调用了数据库,我尝试将其放入一个__construct()函数中,但是它坚持要有参数。
我尝试了下面的代码,但是我得到了以下错误消息: Fatal error: Call to undefined method Database::prepare() in E:\xampp\htdocs\attendance\class.Register.php on line 13 你有什么办法可以调用我的数据库吗?
这是我的代码:

class.Connect.php

<?php

// Database connection PDO

class Database {

    public function __construct() {
        // Connection information
        $host   = 'localhost';
        $dbname = 'attendance';
        $user   = 'root';
        $pass   = '';

        // Attempt DB connection
        try
        {
            $this->pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            //echo 'Successfully connected to the database!';
        }
        catch(PDOException $e)
        {
            echo $e->getMessage();
        }

    }

     public function __destruct()
    {
        // Disconnect from DB
        $this->pdo = null;
        //echo 'Successfully disconnected from the database!';
    }


}

?>

class.Register.php

<?php

require 'class.Connect.php';

class Register {

    public function viewall() {
        $pdo = new Database();

        $stmt = $pdo->prepare('SELECT * FROM users');
        $stmt->execute();

    $stmt->fetch();

    }
}

$run = new Register();
$run->viewall();

?>

7
你的用户是一个数据库吗?不是。那么为什么它会通过测试Users instanceof Database?如果某样东西不是另一样东西,它就不应该去extend它。 - deceze
1
在某种程度上同意@deceze的观点。然而,听起来你正在尝试构建某种ORM,其中你有直接映射到给定DB表的类。在这种情况下,可能有其优点,尽管从语义上讲,如果它是extends DBTable或类似的东西,那么会更好一些,而不是extends Database。但是,如果你正在尝试编写自己的ORM,你可能需要停下来考虑使用已经存在的库(例如Doctrine),它们已经完成了这个工作(并且已经解决了你在编写时遇到的所有问题)。 - Spudley
“如何在不使用继承的情况下,在我的用户类中连接到我的数据库类?” - 可以查看依赖注入 - Benny Hill
3个回答

11

简单的经验法则:如果一个类extends另一个类,那么这个类它的父类(仅略微改变或扩展)。您可以传递这个子类代替父类。例如:

class Foo { }

class Bar extends Foo { }

function baz(Foo $foo) { }

baz(new Bar);

这个可以工作,baz() 期望一个 Foo,但也接受一个 Bar,因为 Bar 一个 Foo

现在,你的 Users Database 吗?不是。你的用户不是数据库。你的用户使用一个数据库。如果需要,你应该使用组合

class User {

    protected $database;

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

}
一个类应该做它的责任所在。用户管理类的责任是管理用户数据。其中一部分可能涉及与数据库交互,但这并不意味着用户管理类就是一个数据库。如果User扩展了Database,那么它可以做到Database类所能做的一切(甚至更多)。这意味着你可以在所有需要使用Database类的地方使用User类,但这没有任何意义。保持责任分离。
现在,这种结构是否正确仍有争议,但它朝着正确的方向发展。但你可能真的想要一个代表一个用户的用户类。然后你会有一个叫做UserManager或UserORM或UserStorage或其他类,它关心从数据库中检索和存储User对象。这个类反过来使用一个数据库来完成这个任务。这样就保持了责任的清晰和分离。User类代表用户数据,Database类与数据库交互,中间的UserORM/Manager/whatever类协调两者之间的关系。

这个网站肯定需要一些珍品来保持和推广这种优秀回答。太遗憾了,该网站的概念禁止这一点 :( - Your Common Sense

-1

连接数据库的方法有很多种,这样你就可以在应用程序的任何地方都能获得一个连接。为了良好的实践,你可以通过使用另一个类来处理数据库操作,并让所有需要数据库操作的类都继承自该类,从而轻松地实现。

首先,稍微修改你的Database类,并按照下面的方式传递数据库连接的参数:

<?php

// Database connection PDO

class Database {

    public function __construct($host, $dbname, $user, $pass) {
        // Connection information


        // Attempt DB connection
        try
        {
            $this->pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            //echo 'Successfully connected to the database!';
        }
        catch(PDOException $e)
        {
            echo $e->getMessage();
        }

    }

     public function __destruct()
    {
        // Disconnect from DB
        $this->pdo = null;
        //echo 'Successfully disconnected from the database!';
    }


}

?>

现在创建另一个类,让我们将其命名为ObjectModel,如下所示。
 class ObjectModel
    {

     protected $db

    public function __construct()
     {

       $this->db = new Database($host, $dbname, $user, $pass) //ofcourse you can get the db connections details and database name from a config file

   }

 // you can have more db operations functions like insert, update, delete etc

 } 

因此,ObjectModel类将处理所有CRUD和其他数据库操作以及运行查询。现在,如果您想使用$db对象,可以直接在ObjectModel类中使用它,也可以在OBjectModel的子类中使用,如下所示:

class Users extends ObjectModel
{

    public function getUsers()
    {
         //$this->db->  whatever operation you want to do you can do it using this db           object
     }
}

请记住,ObjectModel类有一个名为$db的Database类对象。因此,如果您的Database类使用PDO,则ObjectModel $db对象将具有所有这些操作,即使用PDO。

我希望这对您也有所帮助,因为我正在为我的类使用这样的ObjectModel类,它可以节省我大量重复编写代码的时间 :)。

祝编码愉快 ;)


-1
继承类是好的实践吗?
嗯,如果你做得对且出于正确的原因,那就是好的实践。
面向对象编程中继承(类扩展)的概念工作方式与现实世界中的对象完全相同。
在现实世界中,当您使用“汽车”这个词时,通常指的是所有汽车,并且您会考虑其共同属性,例如轮子、引擎、转向、油门、刹车等。 但是有些汽车除了这些属性之外,也有一些额外的属性,例如消防车还带有一些附加装置,如连接式梯子、喷水管等。 同样,某些对象的“行为”也可以考虑在内。例如,汽车可以移动,这意味着您需要模拟汽车在路上的轮子旋转以使其移动(通过Car的move方法建模)。 此外,消防车可能具有扩展行为,例如可以上下移动它所拥有的梯子。这可以通过FireTruckCar类中具有的moveLadder()方法进行建模。
如果您想在PHP类(OOP)中表示这些概念,则可以执行以下操作:
class Car {

    protected $wheels;
    protected $engine;
    //Rest attributes of Car.

    public function __construct($wheels, $engine) {
        $this->wheels = $wheels;
        $this->engine = $engine;
        //Initialize more attributes.
    }
    function move(){
        $this->wheels->rotate();
    }
    //.....

}

class FireTruckCar extends car{

    //Additional Attributes.
    //The attributes of Car class belong to this 
    //one too and are accessible as private.
    protected $ladder;
    protected $tubes;


    public function __construct($wheels, $engine, $ladder, $tubes) {
        //Call the parent constructor, to initialize parent attributes
        parent::__construct($wheels, $engine);
        $this->ladder = $ladder;
        $this->tubes = $tubes;
    }
    function moveLadder($direction){
        $this->ladder->move($direction);
    }
    //...

}

当然,继承和通常的OOP中有许多其他概念,比如方法重载/覆盖、抽象类等等,但我不会在这里解释它们,因为这样会错过重点。我建议你搜索面向对象编程原则,并充分了解它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接