【问题标题】:Why should I abstract my data layer?为什么要抽象我的数据层?
【发布时间】:2011-07-15 22:36:03
【问题描述】:

OOP 原则对我来说很难掌握,因为出于某种原因,我永远无法将它们应用到 Web 开发中。随着我开发越来越多的项目,我开始了解我的代码的某些部分如何使用某些设计模式来使它们更易于阅读、重用和维护,因此我开始越来越多地使用它。

我仍然不能完全理解的一件事是为什么我应该抽象我的数据层。基本上,如果我需要将存储在我的数据库中的项目列表打印到浏览器,我会按照以下方式进行操作:

$sql = 'SELECT * FROM table WHERE type = "type1"';'
$result = mysql_query($sql);

while($row = mysql_fetch_assoc($result))
{
    echo '<li>'.$row['name'].'</li>';
}

我正在阅读所有这些关于 PDO 伟大之处的 How-Tos 或文章,但我不明白为什么。我似乎没有保存任何 LoC,我看不出它会如何更可重用,因为我上面调用的所有函数似乎都封装在一个类中,但做的事情完全相同。我看到 PDO 的唯一优势是准备好的语句。

我并不是说数据抽象是一件坏事,我问这些问题是因为我正在尝试正确设计我当前的类并且它们需要连接到数据库,所以我想我会这样做 正确方式。也许我只是在阅读有关该主题的糟糕文章:)

我非常感谢有关该主题的任何建议、链接或具体的现实生活示例!

【问题讨论】:

    标签: php mysql oop database-abstraction


    【解决方案1】:

    将数据层抽象为将来节省时间的一种方式。

    使用您的示例。假设您更改了表的名称。您必须使用该表转到每个有 SQL 的文件并对其进行编辑。最好的情况是搜索和替换 N 个文件。如果您只需要编辑一个文件,即包含所有 sql 方法的文件,您可以节省大量时间并将错误最小化。

    这同样适用于列名。

    这只是考虑重命名内容的情况。完全改变数据库系统也是完全可能的。例如,您的 SQL 可能在 Sqlite 和 MySQL 之间不兼容。您将不得不再次编辑大量文件。

    抽象允许您将一个部分与另一部分分离。在这种情况下,您可以在不影响视图部分的情况下对数据库部分进行更改。

    对于非常小的项目,这可能比它的价值更麻烦。即使那样,你仍然应该这样做,至少要习惯它。

    【讨论】:

    • 好的,因此更改数据库驱动程序等只需要一些更改,真棒,这是一个很好的例子 :) 谢谢
    • 我觉得优点比我这里解释的要多,但如果只针对这一部分,抽象出来你会收获很多。尝试不要重复自己总是好的。通过抽象,您可以实现这一点。按照您的示例,由于您的所有查询都在一个文件中,因此您不会有 5 或 10 个“从表中选择 *”。您将只有一种方法可以做到这一点。如果您碰巧优化了该方法,那么所有调用它的地方都会立即受益。
    • 完全同意。特别是不要重复自己。有一个完整的模式! =P (en.wikipedia.org/wiki/Don't_repeat_yourself)
    【解决方案2】:

    我不是一个 php 人,但这是一个更普遍的问题,所以这里是。

    您可能正在构建一些小型的东西,有时即使是小型/中型的东西也应该有一个抽象的数据层,这样它才能更好地发展。

    重点是应对CHANGE

    考虑一下,您有一个小型社交网站。想想您将存储的数据、个人资料详细信息、图片、朋友、消息。对于其中的每一个,您都将拥有像 pictures.php?&amp;uid=xxx 这样的页面。

    然后,您将在 mysql 代码中插入一小段 SQL。现在想想改变这一点有多容易/多难?你会改变5-10页?当你这样做时,你可能会在彻底测试之前弄错几次。

    现在,想想 Facebook。想想会有多少页面,你觉得在每个页面中更改一行SQL会更容易吗!?

    当您正确抽象数据访问时:

    1. 它在一个地方,更容易更改。
    2. 因此更容易测试。
    3. 它更容易更换。 (想想如果你不得不切换到另一个数据库你会做什么)

    希望对你有帮助

    【讨论】:

    • 所以我应该创建函数来更新/插入/删除我所有表的行?
    • 是的。或多或少,您应该有一个类来对您的数据执行 CRUD 操作。现在,当您在每个单独页面的代码中使用这些类时,调用 代码不应该知道 数据库,例如DataAccess.MyTable.GetAllType1Records() (Don' t 知道它在 php 中是如何工作的)
    • @Gazillion 也不要过分关注第一次正确的类/代码设计,这也是我犯的错误。你会边走边学。如果你在 SO 中搜索好的 php 数据层模式,我相信你会找到一些东西(或者你可以发布一个关于那个的新 Q =)
    【解决方案3】:

    抽象数据层的另一个优点是减少对底层数据库的依赖。

    用你的方法,当你想使用mysql以外的东西或者你的列名改变或者关于mysql的php API改变的那一天,你将不得不重写很多代码。

    如果所有数据库访问部分都被巧妙地抽象出来,则所需的更改将是最小的,并且仅限于几个文件而不是整个项目。

    如果代码集中在一个地方,重用有关 sql 注入或其他实用程序功能的代码也容易得多。

    最后,如果所有内容都通过某些类进行,而不是在项目的每个页面上进行,那么进行单元测试会更容易。

    比如我最近的一个项目中(抱歉,不能共享代码),mysql相关函数只在一个类中调用。从查询生成到对象实例化的一切都在这里完成。因此,我非常希望更改为另一个数据库或在其他地方重用这个类。

    【讨论】:

      【解决方案4】:

      在我看来,数据访问是从其余代码中分离/抽象出来的最重要方面之一。

      分离出不同的“层”有几个优点。

      1) 它整齐地组织您的代码库。如果您必须进行更改,您将立即知道需要在哪里进行更改以及在哪里可以找到代码。如果您自己处理一个项目,这可能没什么大不了的,但是如果团队更大,好处很快就会变得明显。这一点实际上是微不足道的,但我还是添加了它。真正的原因是第二个..

      2) 您应该尝试将可能需要相互独立更改的内容分开。在您的具体示例中,可以想象您希望在不影响用户界面的情况下更改数据库/数据访问逻辑。或者,您可能希望在不影响数据访问的情况下更改用户界面。如果代码相互混合,我相信你会看到这是不可能的。

      当您的数据访问层具有严格定义的接口时,您可以随心所欲地更改其内部工作方式,并且只要它仍然遵守该接口,您就可以确定它不会进一步破坏任何东西。显然,这仍然需要通过测试来验证。

      3) 重用。编写数据访问代码可能会变得非常重复。当您必须为您编写的每个页面重写数据访问代码时,它甚至更加重复。每当您注意到代码中有重复内容时,警钟就应该响起。重复性,容易出错并导致维护问题。

      我确定您会在不同的页面中看到相同的查询?这可以通过将这些查询放在数据层的较低位置来解决。这样做有助于简化维护;每当表或列名称发生更改时,您只需更正数据层中引用它的一个位置,而不是遍历整个用户界面并可能遗漏某些内容。

      4) 测试。如果您想使用自动化工具进行单元测试,您需要将所有内容都很好地分开。当代码分散在整个界面中时,您将如何测试代码以选择所有客户记录?当您在数据访问对象上具有特定的 SelectAllCustomers 函数时,这会容易得多。您可以在这里测试一次,并确保它适用于使用它的每个页面。

      我会让其他人补充更多原因。最主要的是,分离层允许一个层进行更改,而不会让更改波及其他层。由于数据库和用户界面是应用程序/网站中更改最频繁的区域,因此将它们分开并与其他所有内容以及彼此隔离是一个非常好的主意。

      【讨论】:

        【解决方案5】:

        在我看来,仅打印数据库表中的项目列表,您的 sn-p 更合适:快速、简单和清晰。

        我认为一点更多的抽象可能有助于在其他情况下避免代码重复,并具有所有相关优势。

        考虑一个简单的 CMS,其中包含作者、文章、标签以及文章和标签的交叉引用表。

        在您的主页中,您的简单查询将变成更复杂的查询。您将加入文章和用户,然后您将获取每篇文章的相关标签,加入带有交叉引用的标签表并按 article_id 过滤。

        您将重复此查询,并在作者简介和标签搜索结果中进行一些小的更改。

        使用抽象工具like this,您可以定义一次关系并使用更简洁的语法,例如:

        // Home page
        $articles = $db->getTable('Article')->join('Author a')
            ->addSelect('a.name AS author_name');
        $first_article_tags = $articles[0]->getRelated('Tag');
        
        // Author profile
        $articles = $db->getTable('Article')->join('Author a')
            ->addSelect('a.name AS author_name')->where('a.id = ?', $_GET['id']);
        
        // Tag search results
        $articles = $db->getTable('Article')->join('Author a')
            ->addSelect('a.name AS author_name')
            ->join('Tag')->where('Tag.slug = ?', $_GET['slug']);
        

        您可以减少剩余的代码重复,将其封装在模型中并重构上面的代码:

        // Home page
        $articles = Author::getArticles();
        $first_article_tags = $articles[0]->getRelated('Tag');
        
        // Author profile
        $articles = Author::getArticles()->where('a.id = ?', $_GET['id']);
        
        // Tag search results
        $articles = Author::getArticles()
            ->join('Tag')->where('Tag.slug = ?', $_GET['slug']);
        

        还有其他充分的理由或多或少地进行抽象,各有利弊。但在我看来,网络项目的很大一部分主要是这个:P

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-24
          • 2012-03-30
          • 2020-04-03
          • 2011-02-19
          • 2010-10-15
          • 1970-01-01
          • 2015-06-05
          • 2012-12-20
          相关资源
          最近更新 更多