我一直在尝试学习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,因为它们是来自db的外键。 (或者它们可以事先手动设置,但这不是内存问题,因为它是故意分配。)缺点是所有驱动程序操作都必须通过相同的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的字母而不是它的精神。
再次,谢谢。
答案 0 :(得分:3)
首先,altought,似乎是不必要的,你可以添加一个包含所有其他对象的类,例如&#34; universe&#34;或&#34;世界&#34;。这将帮助您将现实世界建模为一个应用程序。
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
下一步。什么是每个对象&#34;字段&#34; ,&#34;运营&#34;,&#34;会员&#34; ? 你提到,那是&#34;限速&#34;取决于道路。
class RoadClass
{
/* public int */ $SpeedLimit;
} // class RoadClass
/* void */ function Example()
{
WorldClass $MyWorld = new WorldClass();
$MyWorld->Road->SpeedLimit = "50" // ouch, "cow country"
// ...
} // void function Example
对象如何交互。好吧,这里有一个问题。有时对象依赖于其他对象,就像其他对象一样,有时候,对象是独立的,它们只是相关的。
在这个例子中,大多数对象取决于&#34; World&#34;对象,但是,它们只是彼此相关。让我们添加一个小注释来检查。
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
但是,与&#34;外部&#34;进行互动。对象:
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
干杯。
答案 1 :(得分:2)
您通常将该方法放在具有最多信息的类上以完成某项任务。所以在你的情况下,问问自己哪个班级。此外,您应该尝试使类之间的依赖关系变得小而且易于管理,这样您就可以实现低耦合和高内聚。
请参阅GRASP (object-oriented_design)
至于你的具体UseCase,我认为你应该先从编写具体代码中退一步。相反,首先尝试将您想要实现的内容放入普通英语。你到目前为止所说的是:
如果你考虑这个问题,你最终可能会得到一个(不完整的)UseCase:
这应该已经告诉你,拥有Driver,Car和Road不足以将这个UseCase转换为代码。正如您所看到的,还有Location
之类的东西。但是,没有任何关于Location的信息。是GPSCoordindates
吗? StreetAdresses
?或者只是DistanceTraveled
?
我们还可以说你根本不需要Driver
,因为它是应用程序的主要用户,无论如何都需要通过UI完成所有操作。如果是这样,也许应该有另一个域概念:Roadtrip
。这会收集使用过的Car
和Road
或更确切地说Route
。这可以容纳StartingLocation
和许多Roads
,每个SpeedLimit
都有{{1}}。
UseCase没有说的是驾驶员是否总是以最大允许速度行驶,或者车辆是否有可能耗尽汽油的油箱,等等。现在,Car并没有为Roadtrip提供任何东西。我们真的需要吗?因此,在深入研究代码之前,请确保您了解正在建模的问题域。
答案 2 :(得分:1)
您可以研究的另一套软件设计原则是 S.O.L.I.D原则
一起应用的原则旨在使程序员更有可能创建一个易于维护和延长的系统。 SOLID的原理是可以在处理软件时应用的指南,通过使程序员重构软件的源代码,直到它既清晰易读又可以扩展代码气味。
请参阅http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)