【问题标题】:Designing classes & functions for object interaction [closed]为对象交互设计类和函数[关闭]
【发布时间】:2012-02-26 21:15:49
【问题描述】:

我已经尝试学习 OOP 技术和设计模式有一段时间了,我的方法确实有所改进,但是当我们谈论不同类的对象交互时,我并没有完全点击。我主要在 PHP 中工作,但为了学习,我会接受一个一般性的答案。如果我用错了术语,请提前道歉,我是自学的。这是一个例子:

假设我想为开车四处走动的人建模。有 3 个类:

class Car {}
class Driver {}
class Road {}

我希望 Car 类有一个函数

public function Drive($time) {}

其中驾驶应根据道路的速度限制更新驾驶员的位置。我的问题是构建 Drive 以便 Car 与其他对象交互的最佳方式是什么。我能想到 3 种可能性:

public function Drive($time, $Driver, $Road) {}

这里的优点是函数本身告诉我我需要什么参数。缺点是,如果我有很多类似的操作、一路调用私有函数、添加乘客对象等,我最终可能会传递很多参数。

$Car->setDriver($Driver);
$Car->setRoad($Road);
$Car->Drive($time);

优点是我可以非常简单地调用 Drive,它可以访问它需要的任何东西。缺点是我要记得先设置司机和道路,因为函数定义没有告诉我。

class Car() {
  public function Drive($time) {
    $Driver = getDriver($this->DriverID);
    $Road = getRoad($this->RoadID);
  }
}
function getDriver($DriverID) {
  static $DriverArray;
  // check isset(), create object and place in array if not
  return $DriverArray[$DriverID]; 
}
function getRoad($RoadID) {} // assume similar

优点是所有逻辑都是内部的,DriverID 和 RoadID 存在,因为它们是来自数据库的外键。 (或者它们可以预先手动设置,但这不会是内存问题,因为它是有意分配的。)缺点是所有 Driver 操作都必须通过相同的 getDriver 以免我最终得到单独的实例同一个驱动程序。

为了完整起见,我的第四种方法是SELECT <columns> FROM Car_tbl INNER JOIN Driver_tbl on DriverID INNER JOIN Road_tbl on RoadID,我很确定这是糟糕的 OO 设计,但我过去经常这样做,因为它执行的查询更少。

我倾向于方法 3,但怀疑我在很大程度上遗漏了一些东西。随意回答 PHP 特定的或通常如何考虑的。谢谢。


编辑:到目前为止,感谢您的回答,包括进一步阅读的链接。 与其让这个例子过于复杂,让我们现在假设最简单的 UseCase,但稍后假设最复杂的 UseCase。所以例如速度现在是getRoadSpeedLimit() 的函数,但后来受到getMaxCarSpeed()getMaxDriverSpeed() 以及汽车对汽油的需求和驾驶员对食物的需求的限制。如果我们有太多 Driver 对象使用同一条道路等,道路的速度限制会调整。

Gordon 和 UmlCat 似乎(可以这么说)都在使用封装类,对吗?

class RoadTrip_aka_Universe {
  // Operates on instances of Car, Driver, Route (which may contain roads)
}

但这是否仍然存在问题,是否在构造函数中添加了 $Car、$Driver 和 $Route,由 setter 应用,从静态外部提取?无论我们决定哪个类应该有方法,它如何访问那些其他对象?

我的直觉反应是,如果一切都放在一个更大的类中,那么该类的内部是否开始类似于具有全局变量的大型无类结构?不要把它当作批评,我只是想理解,因为它感觉像是 OOP 的文字而不是它的精神。

再次感谢。

【问题讨论】:

  • 我认为这应该问程序员但是,我喜欢选项 2,它读起来就像你给汽车一个司机,你把汽车放在路上,然后告诉它 drive 一些时间,也就是说,虽然汽车可以有司机,但为什么汽车会有一条道路,可能是当前的道路或位置......所以在某种程度上,选项 1 更有意义“与 Y 司机在 A 路上行驶 X 时间”,但是这听起来有点必要,选项 3 不太明确,对我来说,汽车永远不会得到它的司机或道路,不幸的是,我觉得我的评论太抽象而无法成为答案,并期待听到其他人的意见 :)

标签: php oop class design-patterns object


【解决方案1】:

您通常将方法放在具有最多信息的类上以完成某项任务。所以在你的情况下,问问自己那是哪一门课。此外,您应该尽量保持类之间的依赖小且易于管理,这样才能实现低耦合和高内聚。

GRASP (object-oriented_design)

至于您的具体 UseCase,我认为您应该先从编写具体代码后退一步。相反,请先尝试将您想要实现的目标写成简单的英语。到目前为止,您所说的是:

  • 驾车出行的人
  • 驾驶应该更新司机的位置
  • 新位置取决于道路的限速。

如果您考虑这一点,您最终可能会得到这样的(不完整的)用例:

用例 UC1:驾驶汽车

  • 范围:DriveSim
  • 级别:主要任务
  • 演员:司机
  • 优先级:高
  • 前提条件:司机有一个起始位置
  • 后置条件:驾驶员位置已更新
  • 快乐之路:
    • 司机选择汽车
    • 司机选择道路行驶
    • 司机设置总驾驶时间
    • 系统计算总里程
    • 系统更新驱动程序位置
    • 司机下车

这应该已经告诉您,拥有 Driver、Car 和 Road 不足以将此 UseCase 转换为代码。如您所见,还有类似Location 的东西。但是,没有任何关于位置是什么的信息。是GPSCoordindates吗? StreetAdresses?还是只是DistanceTraveled

我们也可以争辩说您根本不需要Driver,因为它是应用程序的主要用户,并且它采取的所有操作都是通过 UI 进行的。如果是这样,也许应该有另一个域概念:Roadtrip。这收集了使用过的Car,以及Road,或者更确切地说是Route。这可以容纳StartingLocation 和许多Roads,每个都有可能的SpeedLimit

UseCase 没有说明驾驶员是否总是以最大允许速度行驶,或者汽车是否有一个油箱会耗尽汽油,等等。现在,Car 没有为 Roadtrip 提供任何东西。我们真的需要它吗?因此,在深入研究代码之前,请确保您了解您正在建模的问题域。

【讨论】:

    【解决方案2】:

    您可以研究的另一组软件设计原则是 S.O.L.I.D 原则

    将这些原则结合使用时,旨在使程序员更有可能创建一个易于维护和随时间扩展的系统。 SOLID 的原则是可以在开发软件时应用的指导方针,通过让程序员重构软件的源代码直到它既清晰又可扩展,从而消除代码异味。

    http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

    【讨论】:

      【解决方案3】:

      首先,似乎没有必要,您可以添加一个包含所有其他对象的类,例如“宇宙”或“世界”。这将帮助您将现实世界建模到应用程序中。

      class CarClass
      {
      
      } // class CarClass
      
      class DriverClass
      {
      
      } // class DriverClass
      
      class RoadClass
      {
      
      } // class RoadClass
      
      class WorldClass
      {
        /* public CarClass */ $Car;
        /* public DriverClass */ $Driver;
        /* public RoadClass */ $Road;
      } // class WorldClass
      

      还有一个小代码示例。可能是这样的:

       /* void */ function Example()
       {
          WorldClass $MyWorld = new WorldClass();
      
          // ...
       } // void function Example
      

      接下来。每个对象“字段”、“操作”、“成员”是什么? 您提到,“限速”取决于道路。

      class RoadClass
      {
         /* public int */ $SpeedLimit; 
      } // class RoadClass
      
      /* void */ function Example()
      {
         WorldClass $MyWorld = new WorldClass();
      
         $MyWorld->Road->SpeedLimit = "50" // ouch, "cow country"
          // ...
      } // void function Example
      

      对象如何交互。好吧,这里有一个问题。有时对象依赖于其他对象,就像是其他对象的一部分,有时,对象是独立的,它们只是相关的。

      在这个例子中,大多数对象都依赖于“World”对象,但它们只是相互关联。让我们添加一个小评论来检查一下。

      class WorldClass
      {
        /* public depends CarClass */ $Car;
        /* public depends DriverClass */ $Driver;
        /* public depends RoadClass */ $Road;
      
        function __construct() {
           // parent::__construct();
      
           // "World" "contains" these objects
           $this->Car = new CarClass();
           $this->Driver = new DriverClass();
           $this->Road = new RoadClass();
        } // function __construct()
      
      } // class WorldClass
      

      汽车有一个登记机,记录移动了多少距离,如果开始、移动和停止。它是一个简单的值,它不是一个对象。

      class CarClass
      {
         /* public int */ $Distance; 
      } // class RoadClass
      

      但是,存在与“外部”对象的交互:

      class CarClass
      {
         /* public int */ $Distance; 
         /* public DriverClass related */ $Driver; 
         /* public RoadClass related */ $Road; 
      
      
        function __construct() {
           // parent::__construct();
      
           // "Car" only relates these objects
           $this->Driver = null;
           $this->Road = null;
        } // function __construct()
      } // class RoadClass
      
      /* void */ function Example()
      {
         WorldClass $MyWorld = new WorldClass();
         $MyWorld->Road->SpeedLimit = "50"; // ouch, "cow country"
      
         $MyWorld->Car->Road = $MyWorld->Road; // a reference only
         $MyWorld->Car->Driver = $MyWorld->Driver; // a reference only
      
          // ...
      } // void function Example
      

      由于汽车的距离取决于道路限速,而变化,当汽车行驶时:

      class CarClass
      {
         /* public int */ $Distance; 
         /* public DriverClass related */ $Driver; 
         /* public RoadClass related */ $Road; 
      
        function __construct() {
           // parent::__construct();
      
           // "Car" only relates these objects
           $this->Driver = null;
           $this->Road = null;
        } // function __construct()
      
      
        /* void */ function Drive($time) {
      
          // general idea of formula:
      
          $this->distance = ($this->SpeedLimit * $time * $this->Road->SpeedLimit);
        } // void function Drive(...)
      
      } // class RoadClass
      
      /* void */ function Example()
      {
         WorldClass $MyWorld = new WorldClass();
         MyWorld->Road->SpeedLimit = "50"; // ouch, "cow country"
      
         $MyWorld->Car->Road = $MyWorld->Road; // a reference only
         $MyWorld->Car->Driver = $MyWorld->Driver; // a reference only
      
         /* int */ $time = 60; // 1 hours in minutes
      
         $MyWorld->Car->Drive($time);
      } // void function Example
      

      干杯。

      【讨论】:

      • 我看到你的意思后,在你完成之前编辑了我的问题。非常感谢您的详细信息。在我看来,在 WorldClass 中的 $MyWorld->Car->Road = $MyWorld->Road 与没有 WorldClass 的 $MyWorld->Car->Road = global $Road 非常相似。前者真的那么优越吗?
      • @Amarsir:它们的工作方式相同。如果对您来说很容易,请使用全局变量。如果你开始progr。全局变量是一种快速的做事方式。但是,最终,使用 O.O.P. 的开发人员制作大型应用程序,并且使用诸如“WorldClass”之类的类,并且有时将每个文件拆分为一个类。干杯
      猜你喜欢
      • 2011-06-02
      • 2015-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-22
      • 2013-09-20
      相关资源
      最近更新 更多