1 回答
TA贡献1884条经验 获得超4个赞
这是我用来谈论该主题的上下文。设函数 f 有以下选项:
interface Y { ... }
class SubY implements Y { ... }
DEFINITION CHOICE:
public SubY f() { ... }
OR
public Y f() { ... }
USAGE CHOICE:
...
Y y = f();
OR
SubY y = f(); //maybe with a cast
...
SubY从技术上讲,所有选项都可以是正确的,具体取决于您是否打算向最终用户(下一个程序员) 公开详细信息。SubY如果向最终用户公开看起来是个坏主意,那就不要这样做。否则,就这样做。推理如下:从概念上讲,您应该始终返回可能需要的最窄类型(较窄的类型位于类层次结构
的更下方- 将其视为“较窄的类型是较宽的类型”)。例如,如果您返回 a ,则意味着您希望最终用户仅通过界面与其进行交互。但是,如果您预计最终用户将需要 中定义的交互,请返回该交互。 一般来说,这个想法是使方法的返回类型尽可能窄,而不暴露最终用户不应该知道的血腥细节。只要您正确隐藏类处理的内部细节,返回. 另一方面,考虑最终用户的责任: 最终用户的责任是负责任地使用狭窄的返回类型赋予他们的权力。您已经通过正确隐藏内部结构来阻止他们做出令人讨厌的事情。然后,当最终用户使用您的类时,他们应该根据自己的需求进行编程,如下所示:ListArrayList
SubYSubY
// imported library provides:
public SubY f() { ... }
... // la la la
Y y = f();
useYFunctionality(y);
... // somewhere else that you need SubY functionality
SubY subY = f();
useSubYFunctionality(subY);
因此,程序员应该定义他们的函数来返回精确的类型,返回尽可能窄的安全实现(可以是一个接口)。另一方面,该服务/功能的最终用户应该将其变量定义为仍然有效的最广泛的可能类型,以减少耦合并提高意图的清晰度(如果这是您所需要的,请使用而getLicensePlateNumber不是Vehicle)VeryUnreliableCar。这里的关键是选择返回类型没有明确的答案。相反,请根据您的判断来决定哪些内容应该暴露给最终用户,哪些内容不应该暴露给最终用户。如果没有特殊原因拒绝访问SubY,请记住以下原则:Functions Return Accurately, Users Define Variables As Necessary或 FRAUDVAN。
将其应用到您的示例中:
class SubY_generalist implements Y
{
public Y f()
{
Y y = new SubY_generalist();
...
return y;
}
}
到处使用接口。当您只关心界面的细节时,这是最好的,对于最终用户来说也是如此。例如,如果您只关心get和add,则Y可能是List,并且SubY_generalist()可能是ArrayList。
class SubY_mix implements Y
{
public Y f()
{
SubY_mix y = new SubY_mix();
...
return y;
}
}
使用特定类型,但在返回时隐式扩展为通用类型。当您实际上需要使用特定类型的方法,但您的最终用户永远不需要这样做时,这是最好的。使用前面示例中的ArrayList和,假设必须调用,一个未在 中定义的方法。那么,显然有必要声明为。但是,由于最终用户只需要,因此您应该返回并隐式扩展。ListftrimToSizeListyArrayListListy
class SubY_specialist implements Y
{
public SubY_specialist f()
{
SubY_specialist y = new SubY_specialist();
...
return y;
}
}
使用专用类型并返回专用类型。这就是FRAUDVAN的“本质”。SubY_specialist由于返回类型允许用户灵活地处理返回SubY_specialist,或者也可以使用f()接口方面的方式Y。当接口不够具体而无法有效使用时(它们应该如此,因为它们是抽象的),这是非常常见的。例如,在 Java类中,只要方法返回 a ,就会发生这种情况。这是因为实施. 每当您使用and 、、、或( Implements )时,也会发生这种情况。 需要这种行为的原因是界面通常不包含最终用户有效利用的足够细节。将这些细节放在接口中会导致接口膨胀/污染,这是不可取的。相反,直接使用负责这些细节的类可以保留具有独特功能的类的单一职责原则(避免被迫在接口的未来实现者中实现冗余)。毕竟,如果您的类实现了两个接口,您要么必须创建一个扩展这两个接口的新接口并返回该接口(这很快就会变得丑陋),要么您可以返回实现者,并让最终用户负责他们需要哪个界面(不难看)。 为什么这种行为如此普遍?这是因为,一旦隐藏了最终用户不需要看到的东西(使用可见性修饰符、封装和信息隐藏),让最终用户以他们选择的方式与您的实现进行交互实际上是一个好主意(即根据他们声明变量的接口)。事实上,我什至声称这里的所有示例都符合 FRAUDVAN,其中向用户公开的“最安全”类型而不是专用类型。在第一个示例中,界面对于用户和最终用户来说足够专业化。在第二个示例中,对于用户来说不够专业化,但对于最终用户来说足够专业化。最后,在这个例子中,对于用户和所有最终用户来说不够专门化,尽管一些最终用户可能选择声明是否适合他们的需要。StreamStreamStreamBaseStreamStringBufferreverseappendreplacedeleteinsertStringBufferCharSequence
YYYYY y = f()Y
所以,这是要点:
添加回答
举报