2017-05-05 57 views
9

我想了解真正定义了紧耦合的相关功能。我有readposts关于这个问题,但有一点仍然不正确的坐我一个number使用抽象接口VS紧密耦合

据我所知,类应该被注入到使用他们的接口,而其具体实现其他类。我也明白,如果一个类遵循一个接口,那么任何使用注入接口的类都可以调用接口中定义的公共函数,并期望类似的功能。

interface iFormatter() 
{ 
    public function format(array $order): array 
} 

public class OrderFormatter implements iFormatter 
{ 
    public function format(array $order): array 
    { 
     // ... 

     return $formattedArray; 
    } 
} 

public class OrderGenerator implements iGenerator 
{ 
    private $formatter; 

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

    public function generate() 
    { 
      // ... 

      return $this->formatter->format($order); 
    } 
} 

所以我觉得在格式化的变化,这将被定义为松耦合的情况;

$example = new OrderGenerator(new CarOrderFormatter); 
$example->generate(); 

$example = new OrderGenerator(new VanOrderFormatter); 
$example->generate(); 

虽然我不太清楚当你将责任从彼此抽象出来但这些类仍然彼此紧密联系在一起。像

$example = new CarOrderGenerator(new CarOrderFormatter); 
$example->generate(); 

$example = new VanOrderGenerator(new VanOrderFormatter); 
$example->generate(); 

事情是,你可以在不同的格式传递给这些发电机,但肯定更经常会如果从generate功能在XXXOrderGenerator具体类期待某些数据format函数内发生而不是一些错误?

所以我相信有抽象以上,虽然接口已经使用我不知道如果这仍是紧耦合的,或者技术上松散耦合最后一个例子把责任落实到自己的类?或者,如果我错过了点......完全是说,当一个类有其他的依赖或依赖于内部或其他的细节要紧密结合

回答

6

类。

在你给OrderGenerator和OrderFormatter不紧密耦合的,因为没有一个是依赖于另一个第一示例的情况下:它们都依赖于iFormatter。另一种说法是OrderFormatter对OrderGenerator一无所知,OrderGenerator对OrderFormatter一无所知,它只知道传递给它的对象实现了函数'format'。当您将CarOrderFormatter或VanOrderFormatter传递给OrderGenerator而没有不利结果时,您已将其突出显示。

在将CarOrderFormatter传递给CarOrderGenerator的第二个示例中,您提到肯定会发生错误,您将VanOrderFormatter传递给CarOrderGenerator。如果CarOrderGenerator中的代码依赖于CarOrderFormatter的实现细节,那么即使iFormatter接口是CarOrderGenerator所看到的,这些类也是紧密耦合的。这段代码会令人困惑,因为CarOrderGenerator用它的'契约'说它不关心格式化器通过了什么,但显然它确实通过了。因此,如果CarOrderGenerator明确表示您只能将CarOrderFormatter传递给其构造函数,那么在代码的清晰度方面会更好。抽象(也就是接口的使用)并不是一件坏事,需要考虑如何定义接口。例如说,而不是只是一个iFormatter接口,而不是你有iCarOrderFormatter和iVanOrderFormatter两者的定义相同,但CarOrderGenerator需要iCarOrderFormatter和VanOrderGenerator需要iVanOrderFormatter。在您的例子可以变成:

$example = new CarOrderGenerator(new CarOrderFormatter 
$example->generate(); 
$example = new CarOrderGenerator(new SportsCarOrderFormatter) 
$example->generate(); 

$example = new VanOrderGenerator(new PassengerVanOrderFormatter 
$example->generate(); 
$example = new VanOrderGenerator(new CargoVanOrderFormatter) 
$example->generate(); 

实现的关键是该接口被引入到创造什么可以传递给发电机没有它造成的问题的灵活性。

+0

谢谢,这件事对我来说很重要 – myol

1

你是正确的,如果碰上汽车/货车混凝土的iGenerator和iFormatter你很可能将有一个不好的时候混合的依赖关系。但事情是,你不应该遇到这种情况,如果你应该回到绘图板,因为你做错了转弯。

我不知道,如果你的例子是人为的还是紧密依托(或没有),以您的问题域。无论哪种情况,这个问题都是糟糕的设计。你的界面应该代表一个单一的责任或目的。为此,iOrderGenerator和iFormatter的目的是什么?你的例子所有iOrderGenerator都会包装iFormatter。我可以重新设计iOrderGenerator,因为它没有任何用处。问题。解决了。

好了,我们不要采取简单的出路,说iOrderGenerator也有一个目的。那么这个目的应该与iFormatter不同。如果您的接口实现跨越其单一目的的边界或者复制另一个接口的目的,那么您的设计可能会很糟糕。举例来说,让我们说iFormatter的目的是根据订单类型(面包车或汽车)以不同的方式打印订单。然后iOrderGenerator负责创建订单(任何类型)。但我可能有不止一种方式来创建订单。 CsvOrderGenerator或XmlOrderGenerator或BulkOrderGenerator(但不包含CarOrderGen或VanOrderGen)。

Nit挑选你的例子,我们可以指出设计问题,解释当你试图演示一个好的代码概念时,你最终会遇到一个不舒服的情况。

你iFormatter是一个糟糕的接口,因为它返回一个模糊值(列)。任何使用返回值的东西都需要深入了解具体如何工作以了解阵列中包含或未包含哪些项目。即我怎么会知道没有读取代码或查看结果(调试/打印)有一个orderId键值。相反,iFormatter应该返回一个固定的良好描述类型,即CarFormat或VanFormat,其中每一个都可能实现一个提供订单号和价格的常见访问器的iFormat。

iOrderGenerator也许应该产生iOrder。其中可能有一个方法getFormatter:iFormatter。

这些更改可能会清理设计,并为每个具体情况指明更明确的目的,这些目标不会将您引导至初始状态。

+0

谢谢你关于模糊返回值的观点,这改变了我对如何完全构造类的看法。我觉得你也应该得到你的解释的赏金。 – myol