PHP:现实世界中的面向对象编程示例

52

我正在尝试学习面向对象编程。我正在阅读的书中所谓的“真实世界”示例没有帮助。

像“Pet”、“Car”、“Human”等所有示例都已经不能再帮助我了。我需要类似注册、用户个人资料页面等的真实生活示例。

例如:

$user->userName = $_POST['userName'];//save username
$user->password = $_POST['password'];//save password
$user->saveUser();//insert in database

我也见过:

$user->user = (array) $_POST;

where:

private $user = array();

存储所有信息的数组。

同一个类内还包含了...

$user->getUser($uid);
// which sets the $this->user array equal to mysqli_fetch_assoc() using 
//the user id.

在许多不同的PHP应用程序(注册,登录,用户帐户等)中是否有任何真实世界的面向对象编程(OOP)实例?


请参阅:“无行话比较面向对象编程与过程式编程”:https://dev59.com/vHI_5IYBdhLWcg3wFu_L - dreftymac
8个回答

87
面向对象编程(OOP)不仅关乎单个类的外观和操作方式,还涉及一个或多个类的实例如何协同工作。这就是为什么你会看到很多基于“汽车”和“人”的例子,因为它们确实很好地阐述了这个原则。
在我看来,OOP中最重要的课程是封装(encapsulation)和多态(polymorphism)。
封装: 将数据和对该数据进行操作的逻辑紧密地耦合在一起,以简练、逻辑的方式。 多态: 一个对象能够像另一个对象一样看和/或行为。
一个很好的现实世界的例子就是目录迭代器(directory iterator)。这个目录在哪里?也许它是一个本地文件夹,也可能是远程的,比如FTP服务器。谁知道呢!
以下是一个演示封装的基本类树:
<?php

interface DirectoryIteratorInterface
{
    /**
     * @return \Traversable|array
     */
    public function listDirs();
}

abstract class AbstractDirectoryIterator implements DirectoryIteratorInterface
{
    protected $root;

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

class LocalDirectoryIterator extends AbstractDirectoryIterator
{
    public function listDirs()
    {
        // logic to get the current directory nodes and return them
    }
}

class FtpDirectoryIterator extends AbstractDirectoryIterator
{
    public function listDirs()
    {
        // logic to get the current directory nodes and return them
    }
}
每个类/对象都负责其自己的目录列表检索方法。数据(变量)与使用数据的逻辑(类函数即方法)耦合在一起。
但故事还没有结束 - 记得我说过OOP是关于类实例如何协同工作,而不是单个类或对象吗?
好的,那么让我们对这些数据做些什么 - 将它们打印到屏幕上?可以。但是怎么做呢? HTML? 纯文本? RSS? 让我们来解决这个问题。
<?php

interface DirectoryRendererInterface
{
    public function render();
}

abstract class AbstractDirectoryRenderer implements DirectoryRendererInterface
{
    protected $iterator;

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

    public function render()
    {
        $dirs = $this->iterator->listDirs();
        foreach ($dirs as $dir) {
            $this->renderDirectory($dir);
        }
    }

    abstract protected function renderDirectory($directory);
}

class PlainTextDirectoryRenderer extends AbstractDirectoryRenderer
{
    protected function renderDirectory($directory)
    {
        echo $directory, "\n";
    }
}

class HtmlDirectoryRenderer extends AbstractDirectoryRenderer
{
    protected function renderDirectory($directory)
    {
        echo $directory, "<br>";
    }
}

好的,现在我们有了几个用于遍历和呈现目录列表的类树。 那么我们该如何使用它们呢?

// Print a remote directory as HTML
$data = new HtmlDirectoryRenderer(
  new FtpDirectoryIterator('ftp://example.com/path')
);
$data->render();

// Print a local directory a plain text
$data = new PlainTextDirectoryRenderer(
  new LocalDirectoryIterator('/home/pbailey')
);
$data->render();

现在,我知道你在想什么:“但Peter,我不需要这些大的类树来做这个!” 但是如果你这样认为,那么你就错了,就像我怀疑你对“Car”和“People”示例一样。 不要抓住例子中的细节 - 相反,试着理解正在发生的事情。

我们创建了两个类树,其中一个(*DirectoryRenderer)以预期方式使用另一个(*DirectoryIterator),这通常称为一个合同*DirectoryRenderer的实例不关心它接收到哪种类型的*DirectoryIterator实例,*DirectoryIterator的实例也不关心它们如何被渲染。

为什么? 因为我们已经设计好了它们。 它们只需插入彼此即可正常工作。 这就是面向对象编程(OOP)的实际应用


有没有用户的示例?我希望有一个关于管理和认证的示例。如果有人知道在哪里可以找到,我会非常感激。 - iLevi
很遗憾,我的问题已经被关闭了,但如果你能帮我解答这个问题(链接在此),我会非常感激。 - iLevi
这很不错,但如果它还能显示实际的多态性或组合而非继承,那就更好了。在接口中,渲染可以只是一个存根。 - markus

9
购买一本像“PHP和Mysql everyday apps for Dummies”这样的书。虽然它有些陈旧[2005年出版],但是其中涵盖了用户登录、论坛、购物车等概念,并以Procedural和使用Mysqli的Object Oriented两种方式进行演示。
它帮助我学习了Object Oriented PHP,我花费了很多时间学习它,绝对物有所值。
面向对象编程(OOP)非常类似于将程序的各个部分分组为可重用的模块。老实说,这并不难,只需要将函数打包成类即可。
下面是一个关于OOP的现实世界微型例子:
数据库类
会话类
Web表单类
电子邮件类
账户类(以下是示例函数)
选择账户
检查密码
检查用户名
创建账户
我希望你可以坚持下去,PHP 6将重新设计以更好地支持OOP。
祝你好运!

2
尽管我知道这个问题已经被回答了,但我觉得我可以在这里添加价值。
我认为你不应该使用PHP作为学习面向对象编程语言的工具。如果你想学习用于Web应用程序的面向对象编程,你应该真正地关注C#或Java。一旦你学会了面向对象编程,然后你就可以将这些知识应用到PHP上。我用来学习面向对象编程的书籍之一是Cay S. Horstmann的《Big Java》。
为什么我这么说呢?因为PHP有成千上万的例子可以教你如何做事情,但很少有例子可以教你如何正确地编程。此外,PHP允许你采取许多捷径,而这些捷径在像Java这样的语言中是不可接受的。因此,如果你以Java的思维方式来编写PHP,我相信你会成为一个更强大的程序员。面向对象编程不是特定于某种语言的,它是一种编程范例。
如果你必须使用PHP学习面向对象编程,我建议你查看github上公共代码库中的一些真实源代码。你可以在packagist.org中搜索它们。如果它们是一个像样的公共代码库,它们将包含一个readme.md文件,该文件将显示如何使用composer包。例如https://github.com/moltin/laravel-cart是一个购物车包的示例,你可以在你的应用程序中使用它。注意,你不需要查看包的源代码来了解这些包的作用。包已经被编写好了,你不关心它们是如何工作的,但你使用它们,所以你只需要知道如何使用它们。这正是面向对象编程的精髓。
引述:

我不关心购物车类如何向购物车添加商品,我只想创建一个新的购物车并向其中添加东西。

然而,你正在深入源代码作为了解面向对象编程的工具。
此外,对于Web应用程序开发,更重要的是,我会研究MVC设计模式。
MVC设计模式代表了模型(Model)、视图(View)和控制器(Controller)。在Web应用程序中,模型负责建模数据库,视图负责向用户显示内容,控制器负责与模型交互并处理用户输入。
我认为你应该尝试在本地机器上安装Laravel Framework或其他“体面的现代框架”。为什么要选择现代框架?因为现代框架需要最低PHP版本为5.3+,这意味着你的机器上的PHP将支持真正的面向对象编程,类似于Java。
有许多教程可以向你展示如何在Laravel中构建Web应用程序。当你创建一个控制器时,你会发现它继承自BaseController。当你创建一个模型时,它继承自Eloquent。这意味着你已经在你的代码中使用了多态性。你会看到通过使用类,它们被封装,并且每个类都有特定的角色。
当您需要与数据库交互时,您将在控制器方法中最初创建一个新模型对象。随着您学习的深入,您将开始学习如何将依赖项注入控制器,然后学习如何转储您的模型并创建存储库以及按接口编程。
一本适合初学 Laravel 的不错书籍是 Dale Rees 的 https://leanpub.com/codebright。我大约两周前在 Laravel 社区见过 Dale。
此外,随着您在构建 Web 应用程序方面变得更加熟练,您将开始学习如何将以下原则应用于您的编程:
  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖反转原则

1

你说得对,在网上找到的99%的教程都太基础了,而且不太有意义。我建议任何想学习面向对象编程的人:

找一些你已经用过过程式编程写过的代码,尝试将其转换为面向对象的代码。

将所有变量作为类的属性,并找到合适的类名。然后将所有函数(如果有)分组放入一个类中。如果你编写的代码没有使用函数,请首先尝试将代码转换为一组函数。找到合适的函数名称,函数应该定义代码的功能。一旦你有了函数,请将它们移动到一个类中。

我会给你一个简单的分页代码示例,我将其转换为可重复使用的分页类。在YouTube上找到完整的教程:https://www.youtube.com/watch?v=X38IRlyY_ww&t=91s,源代码链接在说明框中。

class Paginator{

    /*the class need to know the current page to calculate the offset
       the offset tell my sql how many rows to skip */
   private $current_page;
   //the limit is the number of results to display per page
   private $limit;
   /*pagination links to display below the results with next 
   and previous button*/
   private $pagination_links;
   //the last page of your pagination links 
   private $last_page;
   .
   . etc

    public function __contruct($number_of_rows_found,$results_to_display_per_page){

      //the paginator uses the number_of_rows_found to determine the last page 
       $this->last_page = $number_of_rows_found/$results_to_display_per_page;
          //get the current page, set it to 1 by default
         $this->current_page = isset($_GET['page']) ? ($_GET['page'] : 1;

    }

   public function generate_pagination_links(){ 
          this method uses the last_page property to generate pagination links
           if the last page is 3, this will give you 3 pagination links
         for ($page = 1; $page <= $this->last_page; $page++) {
            //use
         }
   }
public function get_offset_and_limit(){
     //this method calculates the offset based on the current page
      return "LIMIT $this->per_page OFFSET ".($this->page - 1) * $this->per_page;
 }
.
.
.etc
}
}

要使用分页类,您需要创建一个新实例,并将每页要显示的结果数以及查询返回的结果数作为参数传递。分页类将为您生成分页链接并计算偏移量和限制。这是一个很好的php可重用类的示例,您可以在多个项目中使用它,而无需重新编写或更改它。

$paginator = New Paginator($rows_found,8);
    $pagination_links = $paginator->get_pagination_links();

    $offset_and_limit =  $paginator->get_offset_and_limit();

    //apend the limit and offset to your sql query
    $query = $query. ' '.$offset_and_limit;

    $connection = getdbconnection();
    $stmt = $connection->prepare($query);
    $stmt->execute();
    $movies = $stmt->fetchAll();

当然,面向对象编程中还有更高级的概念没有在这个例子中涉及,但这应该让你基本了解类和对象是如何工作的。

1

我建议你现在远离任何框架,如果你不懂面向对象编程(OOP),深入研究Zend或其他框架会太过困难。

PHP OOP有点有趣...就像哈哈有趣一样,因为它是被支持的,但PHP不像Java或C#那样是一种面向对象的语言。

以下是一个简短的例子,以强调我的观点:

// define class
class User {
// define properties and methods
public $name = "";
}
// instantiate class
$user = new User; // or new User() or new user, it's all the same
echo $user->name;

但是如果你想要在程序运行时进行面向对象编程,可以按照以下步骤操作:

$user = (object) array('name' => 'Peter');

然后

$user->name;

但是你可以像在Java或C#中一样使用OOP,但不会达到同样的程度 - 要记住,像WordPress和Drupal这样的流行系统并不是纯粹的OOP!但是你也可以在PHP中实现继承和其他类似OOP的功能。


13
抱歉,但是$user = (object) array('name' => 'Peter');并不是面向对象编程(OOP),甚至不能称之为“即时面向对象编程”。那只是一个对象。使用对象并不等同于面向对象编程。显然,对象的使用是面向对象编程的一个重要组成部分,但它远不止于此。 - Peter Bailey

1

我在 PHP 面向对象编程方面还没有深入探究,但我越深入了解,就会变得越容易。对象示例只是为了帮助您理解面向对象编程的工作原理。我已经理解并学过这个,面向对象编程只是涉及属性和方法(普通变量和函数)。我自己进行了真正的面向对象编程,应用了教程中的示例,不一定非得在现实世界中。那就像被喂饱了一样,你永远不会理解面向对象编程,并且很容易忘记。我的建议是要去理解。如果你理解了,你就可以做任何事情,并意识到面向对象编程的威力。我下载了这本书,我想你也应该这样做。但这就像有人为你构建应用程序一样......

这里是一本《PHP和MySQL日常应用入门》的书籍链接


1

正如astropanic所说,您可以查看一些优秀的PHP框架或库的源代码。我推荐Zend Framework,它非常模块化,并且具有出色的专业设计。我认为这是一个非常好的面向对象的PHP代码。

不过,我认为从大量的生产代码中学习并不容易,因为它并不是真正为了教你而制作的。但是,如果您想要真实世界的面向对象的PHP代码,Zend Framework(或Symfony,或者可能是CakePHP)可能是最好的选择。


0

我建议您也看一下我的封装 Arrayzy。它是一个原生的PHP数组易于操作的OOP库。

因此,如果您使用原生的PHP数组函数 - 您可以以OOP方式完成相同的事情,并且Arrayzy可以帮助您,例如:

// Functional programming:
$array = ['a', 'b', 'c'];
$resultArray = array_merge($array, ['c', 'd']);

并且

// Object-oriented programming:
$obj = Arrayzy\MutableArray::create(['a', 'b', 'c']);
$obj->mergeWith(['c', 'd']);
$resultArray = $obj->toArray();

在这两种情况下,结果数组将是:
Array(
    0 => 'a'
    1 => 'b'
    2 => 'c'
    3 => 'c'
    4 => 'd'
)

检查一下这个mergeWith方法(或其他方法)在底层是如何工作的。

我认为这是一个很好的例子,它展示了几乎所有函数式代码都可以像这个库中的OOP代码一样替换。但是使用OOP,你会得到更多,你还可以查看函数式编程 vs OOP问题,以了解更多细节和优缺点。


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