7

我经营一个街机网站,在过去的几年里,它添加了许多功能,到了程序编程看起来太复杂的地步,添加新功能或进行简单的设计修改可能非常棘手。

因此,我决定尝试使用相同的功能但以 OOP 格式从头开始重新编码该站点。

我遇到的问题是选课,我了解 OOP 以及它应该如何工作,但似乎总是难以入门。我不确定我是否应该尝试为类(例如具有登录用户功能的用户类)创建功能,或者用户类是否应该只是添加/更新/显示用户详细信息并且登录部分在系统中会更好班级?

目前我已经开始使用以下类和函数,但它们是否适合这个类别?

<?
class User {

    var $userId,
        $username,
        $userRole,
        $userEmail;

    function isLoggedIn(){

    }

    function login($postusername, $postpassword)
    {

    }

    function increaseLoginCount(){

    }

    function logout(){

    }
}
?>

然后我可以在 page.php 中包含类似以下内容 .. (未显示连接类)

<?
$db = new Connect;
$db->connect();

$user = new User;

if(!$user->isLoggedIn())
{
    echo "Please Log In.";

    if($_POST['user'])
    {
        $user->login($_POST['username'], $_POST['password']);
    }
}
else
{
    if($_POST['logout'])
    {
        $user->logout();
        exit;   
    }

    echo $user->username." Logged In.<br />";
}
?>

但是随后该站点将具有显示游戏类别的页面,并且我不知道 displayGames() 函数适合哪里,因为它不是单个游戏,所以不会进入“游戏”类?

我试图找到“现实世界”的例子,但向我展示如何让大象改变颜色或跳舞的 php 代码并没有真正帮助......

4

4 回答 4

9

让我们从一些文本分析开始,我强调:

我经营一个街机网站过去的几年里

想想你能应付多少以及你正在处理什么。还要了解,多年来您已经获得了有关运行街机站点的特定知识。你已经获得了你所在地区的专业。永远不要低估自己的职位和资产,这是您运营的基础,您将对其进行更改。这包括您网站的用户群。

它添加了许多功能,程序编程看起来太复杂了,添加新功能或进行简单的设计修改可能非常棘手

如果系统增长,它们会变得越来越复杂。这不仅仅特定于过程编程,这是一个事实。由于您已经运行该站点多年,因此您知道事情发生了怎样的变化,尤其是在用户如何与您的站点交互方面。

因此,我决定尝试使用相同的功能但以 OOP 格式从头开始重新编码该站点

据说可以使用 OOP 技术来制作可重用的软件,有(并且不可能)任何证据证明这一点。

但是在商业软件开发中,从头开始重写整个应用程序是成功的例子很少。很少。商业软件开发的规则可能不适用于您的特定情况,所以只是说。

在重新编码网站之前请三思。做很多工作只是为了达到同样的目的是有些徒劳的,而且可能会令人失望。相反,您可能更具体地查看您当前的设计中的哪个引入了您希望改变的最大问题。

PHP 可以混合过程和面向对象的风格,这在你有遗留代码时特别有用(遗留代码的常见定义是没有自动化测试的代码)。

我的问题是选课

我试着改写一下:为了什么写课程?

了解 OOP 以及它应该如何工作,但似乎总是难以入门

开始总是最艰难的一步。您现在可以做出决定,但您无法展望未来。通过消除风险最大的部分来降低风险。您可能已经开始在这里提出问题以获得一些反馈以作为您的决定的依据,但这可能不会减轻负担并可能导致混乱。然而,接受教育通常是一件好事。

我不确定我是否应该尝试为类(例如具有登录用户功能的用户类)创建功能,或者用户类是否应该只是添加/更新/显示用户详细信息并且登录部分在系统中会更好班级?

这在很大程度上取决于您的应用程序的性质和用户的性质。可能您的大部分脚本只需要知道用户是具体的还是匿名的(匿名用户尚未登录),它的 ID、名称或昵称。

应用程序应该将该用户提供给任何消费组件,因此每个命令/脚本不需要处理 a) 获取用户信息和处理用户(如登录),b) 验证组件是否对用户有效(访问控制)。这应该放在其他地方,例如在应用程序控制器PofEAA中。

每个具有应用程序控制器对象的脚本/命令都可以请求用户并与用户交互。

然而,这只是从技术上讲。处理自己不确定的事实,处理这些信息。尝试更好地表述你的具体问题,列出具体解决方法的优缺点,在开始编码之前再次远离具体代码。然后比较优劣。试着让事情变得更简单,更简单。

可能用简单的文字写下应该发生的事情,而不是编写代码。

目前我已经开始使用以下类和函数,但它们是否适合这个类别**?**

你的代码很简单,所以很难说太多 - 特别是因为我不知道你的街机网站是什么(以及你写的类别是什么)。它可能仍然是一个很好的例子。在您的课程中可以看到的是,您将所有内容紧密集成在一起。

例如,您从数据库开始。这很常见,因为数据库是任何应用程序的中心组件。应用程序需要数据库才能运行。但是,您希望保持松散耦合,以便您的所有命令都可以与其他数据库或与其他数据库连接的新用户对象一起运行,而不是应用程序的其余数据对象。

 $application->getDB();

由于用户是每个应用程序的中心主体,它应该有一个非常简单的界面。所有关于身份验证、检索用户属性等的荣耀细节都应该委托给另一个类/组件,以便您可以更改存储用户的实现以及他们如何进行身份验证:

 /**
  * @package command.modules.games
  */
 function ListGamesCommand(Request $request, Response $response)
 {
     $application = $request->getApplication();
     $user = $application->getSession()->getUser();
     $games = $application->getModels()->build('games');
     $games = $games->findByUser($user);
     $response->setProp('games', $games);
 }

如本示例所示,您可以在需要时添加功能。例如,只要您的应用程序不需要实际登录用户,为什么要关心它现在是如何编写的呢?

创建一个为应用程序对象生成用户的工厂——无论它现在或将来需要什么(参见The Clean Code Talks — Inheritance, Polymorphism, & Testing中的两堆对象)。如果您随后需要身份验证,请将其添加到会话对象或用户对象接口。

无论如何,身份验证本身将在它自己的类中实现,Authenticator. 因此,您也可以稍后通过将身份验证调用从会话转移到用户或其他方式来查看您的接口。只有一小部分命令需要处理这些特定任务,并且由于所有新代码都在自动测试中,因为您希望重写并从 OOP 中受益,因此您已确保所有地方都被覆盖并正确重构。

访问请求变量也是如此。如果你想利用 OOP 的好处——它与间接高度相关(并且每一层间接都有代价)——你首先应该让你的基类对特定数据而不是任何数据进行操作(比如全局变量和超全局变量,我$_POST在您的示例代码中看到了)。

因此,使您的新代码能够对请求进行操作并提供响应(输入 - 处理 - 输出):

$request  = new Request($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES, $_ENV);
$response = new Response;

取自BankAccount 的示例 - 用于 PHPUnit 培训的示例应用程序

现在这下面的所有东西都可以在一个Request和一个Response对象上运行——处理输入并将其转换为输出。域命令(执行此操作的脚本/命令)不再需要关心从 HTTP 请求中提取输入,例如使用$_POST,或者$_GET他们可以直接从Request- 或者如果你编写它自己的一类命令 -这可以更加量身定制。有些命令可以对自己的请求和响应进行操作。

下一个重要主题是用户界面。你写你想:

我决定尝试使用相同的功能但以 OOP 格式从头开始重新编码该站点。

我已经写过这样的做法可能是徒劳的。受益于 OOP 代码意味着下次更改代码时,您仍然可以重用组件。随着软件的不断变化,这一次已经是下一次了。因此,您希望已经重用现有代码。我假设您现有代码的一部分是输出逻辑。因此现有的输出逻辑需要与示例RequestResponse以上接口。

我打赌你喜欢网站。你喜欢让它们正常工作并且看起来很棒。多年来,您已经建立了自己的网站,即使并非一切都像您希望的那样,您也不想放弃它。因此,对于您的重写至关重要的是,您不会破坏所有内容,但您可以从现在到将来保留它的工作形式(另请参阅保留工作应用程序;列表的最后一点)。

在 webapps 中,视图是非常关键的部分。如果您失去视图,您的网站将失去它的身份。如果您对其进行过多更改,您的网站将失去今天习惯使用它的用户。

如果你打破它,

  1. 你会注意到吗?
  2. 你能修好它吗?

另一方面,您希望您的应用程序代码(特性、功能)不再与它紧密绑定,以便在重写代码时受益。当你想重写你的应用程序时,让我们看一下:

     .---------.                            .-------------.
     | website |  ---> [interface in ] ---> | application |
     | user    |  <--- [interface out] <--- |             |
     `---------´                            `-------------´

如该架构所示,为了使您的应用程序更加独立于交互的样子(可以是网站、(智能手机)GUI 或票务系统),应用程序代码应该是可替换的。您不想编写逻辑来获取用户游戏,例如在新的应用程序代码中为每种类型的用户界面,但您在旧的应用程序代码中做了。

这里以User对象为例。它的身份验证方式和存储位置不应该是您的新应用程序命令代码关心的问题。如果命令需要它,它就在那里。不是全局的,而是特别是如果命令要求它。

其中,注册和丢失密码程序是您现有应用程序的一部分并继续存在。

现在您需要将旧代码和新代码放在一起。

因此,您可能会从 HTTP 请求和 HTTP 响应的接口开始。该视图以该Interface Out启动。您通过该接口为视图分配/传递所有需要的数据,您的应用程序不再知道该视图。您无需处理新应用程序代码中的任何 CSS、Javascript 或 HTML 代码。这只是输出的糖。您的应用程序也应该通过控制台/telnet 以纯文本形式或作为远程 XMLRPC 服务、AJAX 端点进行接口 - 随便什么。

因此,您可能只是概括您的视图代码并向其注入变量。编写一个视图层可以像包含一个 PHP 文件一样简单。它对在其范围内可用的变量进行操作。它可以利用其范围内可用的“帮助”功能(模板宏)。它可以利用视图模型对象。甚至可以为视图编写自己的语言(模板语言,领域特定语言 (DSL))。

但这只有在您创建一个允许应用程序代码执行此操作的接口时才有可能。

因此,您现在要做的就是将 HTTP/HTML/CSS/JS 从您的应用程序移到它自己的适配器中。该适配器能够制定可以通过接口传递给任何应用程序的通用命令。

应用程序只会注意执行命令并通过接口输出它的响应。所以你现在有两个域:你的应用程序和网站。

您可以开始创建这两个新域,然后为旧代码和新代码提供一个进出接口。

您还有“两个”应用程序并排放置。它们最终与您的数据库绑定在一起(在他们自己的代码中不可见),该数据库负责您网站的数据是否有序。这就是数据库的用途。将数据与代码分开,这样您就可以随着时间的推移更改代码。

此外,如果您想重新编码,请在现有代码和新代码之间画一个边界。

祝你好运!我希望阅读本文会为您展示一些针对您的具体情况的选项。另请注意,您不会将控制器变成数据库的另一个外观。通过使用轻量级 HTTP 抽象和仅视图层,您可能获得了最大的好处(不知道您的具体最大问题),因为您的应用程序可能仅适用于网站。

因为在 HTTP / PHP 中:

[Interface In]  Plain Text HTTP request
[Application]   Free to go
[Interface Out] Plain Text HTTP response

您通常只需要一些功能来解析输入并构建输出。

此外,不使用胖模型的好处是您可以快速、按顺序访问数据,例如,如果您不需要一次传递输出(缓冲、一个块),您可以利用将输出流式传输到服务器。

您应该决定哪些部分对您的应用程序重构很重要,而不是 OOP 与否。与程序一样,OOP 也需要做好。如果您今天在编写过程代码时遇到问题,那么 OOP 代码可能无法解决您的问题。可能需要编写更好的程序代码。只是说,重构应用程序并不容易,您应该首先确定实际问题。

  1. 如果你打破它,你会注意到吗?
  2. 如果你打破它,你能修复它吗?

关键部分是您可以注意到并且您手头有一切可以进行修复。

让您的网站接受测试,这样您就可以说在这里或那里更改代码是否真的很好。如果发现它不起作用(更好),则能够将任何更改返回。

完成后,您可以轻松决定是否使用新功能。只要您不需要引入新功能,您编写功能的方式就无需更改任何内容。在此之前,您无法计划新功能。

所以最好在重新编写你的应用程序之前三思而后行。正如所写,这可能会杀死项目。


另请参阅: 如何在我的 PHP/SQL/HTML/CSS 代码上实现 MVC 样式?问答

于 2012-01-03T12:03:48.623 回答
7

OOP 是关于识别责任领域和构建独立的代码单元来处理这些领域中的一个,而且只是其中一个。一般的经验法则是,系统中的每个对象都应该体现现实世界中的等效对象或概念,但这并不总是正确的,因为您还需要担心使系统工作所需的抽象事物(我的意思是抽象的在这里从某种意义上说,它们不代表业务逻辑项,但仍然需要使系统工作。我不是指抽象类,这完全是另外一回事)。

例如,在您的游戏网站中,您可能必须处理游戏、用户、版主、帐户、评论、评论等。其中每一个都应该是一个独立的类,并且该类的每个实例都应该代表一个特定的用户、游戏、评论等。

但是类有责任范围,如上所述,一个类应该处理它的责任范围,而不是别的。呈现页面不是上述任何对象类的责任。这就是不代表系统实体的类的来源。您可能需要一个用于 Page 的类、一个用于 Session 的类、一个用于数据库连接的类(尽管 PHP 已经用 PDO 覆盖了那里)以及其他一些数据库模块,例如 mysqli)。

要呈现页面,您将使用页面类的实例。您将向它传递对已登录用户对象的引用,对您希望它显示的任何游戏对象的引用等等。然后你让它呈现实际的 HTML。Page 类不需要了解您传递给它的对象的内部工作原理,除了那些对象公开的 API(在 OOP 圈子中称为对象的协议,换句话说,它们的公共方法和属性)。一个(非常基本的)Page 类可能如下所示:

class Page
{
    private $user = NULL;
    private $games = array ();

    public function setUser (User $user)
    {
        $this -> user = $user;
    }

    public function getUser ()
    {
        return ($this -> user);
    }

    public function addGame (Game $game)
    {
        $this -> games [] = $game;
    }

    public function getGames ()
    {
        return ($this -> games);
    }

    public function generate ()
    {
        $user = $this -> getUser ();
        $games = $this -> getGames ();

        $pageFile = '/path/to/a/php/script/representing/the/page/markup.php';
        require ($pageFile);
    }

    public function __construct (User $user, array $games)
    {
        $this -> setUser ($user);
        foreach ($games as $game)
        {
            $this -> addGame ($game);
        }
    }
}

实际的 markup.php 脚本可能看起来像这样:

<html>
    <head>
        <title>Games page for <?php echo ($this -> user -> getName ()); ?>
    </head>
    <body>
    <p>Hello, <?php echo ($this -> user -> getName ()), here are your games.</p>
    <?php if (count ($this -> games)) { ?>
    <ul>
        <?php foreach ($this -> games as $game) { ?>
        <li><?php echo ($game -> getName ()); ?>: your best score is <?php echo ($game -> getHighScore ($this -> user)); ?></li>
        <?php } ?>
    </ul>
    <?php } ?>
    </body>
</html>

您可能已经注意到,如果您使用这种方法,那么您的应用程序的模块将倾向于属于以下三个类别之一。您的业​​务逻辑对象(如 User 和 Game)、显示逻辑(如 markup.php 文件)以及用作粘合逻辑和协调形式的第三组(如 Page 类)。

虽然在这种特殊情况下它特定于您的站点,但如果您要进一步推广这种方法,它将落入一种称为 MVC 的设计模式,它代表模型、视图、控制器(实际上它更接近于一种称为 PAC for Presentation 的模式, 抽象, 控制器, 但由于某种原因在 PHP 社区中几乎总是称为 MVC, 所以我们现在只说 MVC)。模式是程序员经常遇到的一类问题的概括,因此拥有一个预制解决方案的工具包非常方便。

在您的游戏应用程序中,User 和 Game 是模型,Page 是控制器,markup.php 是视图。如果您将不同的 markup.php 脚本替换到此代码中,您可以使用它以完全不同的方式呈现完全相同的数据,例如作为 XML 文件。您还可以使用具有不同控制器的相同模型来让它们做不同的事情。这里要记住的重要一点是,模型不应该关心它们是如何被使用的,这样它们就可以根据控制器需要实现的目标以不同的方式使用。

由于 MVC 是一种模式,因此已经存在用于在 PHP 中构建 MVC(尽管它们不是真正的 MVC ;))应用程序的工具包。这些被称为框架。有很多可供选择,例如 Symfony、CodeIgnitor、Zend Framework 等。现在最流行的框架是 Zend,尽管我个人并不喜欢它。(我会说 Zend Framework 2 的 beta 版本看起来确实比当前版本的框架好很多)。

我希望这对你有所帮助。我知道 OOP 一开始可能会让人望而生畏。它确实需要你改变作为程序员的思维方式,但别担心,它会伴随着足够的练习。

于 2011-12-31T07:54:17.267 回答
0

用户类中的所有成员看起来都属于那里。将 gui 代码与其他代码分开很重要,我会将 displayGames() 放在某种 gui 类中。

于 2011-12-31T04:15:00.633 回答
0

GordonM 的帖子可以帮助您入门。我当然会建议您使用已建立的框架来帮助您入门 - 它们为您完成了很多繁重的工作,并将帮助您习惯 PHP 中的 OOP。就个人而言,如果您使用的是 PHP5.3,我会推荐 Symfomy2,但这纯粹是个人喜好。我还建议您获得一本“四人帮”的书。这是非常重要的阅读材料,尽管它主要来自非请求驱动的环境背景,但许多模式在 PHP 中仍然相关。

于 2011-12-31T11:04:17.073 回答