为了账号安全,请及时绑定邮箱和手机立即绑定

如何在方法的使用和定义之间灵活地保留有用的对象功能?

如何在方法的使用和定义之间灵活地保留有用的对象功能?

四季花海 2023-10-13 10:01:40
说明:假设我有这个简单的界面:interface Y { Y f(); }我可以通过 3 种不同的方式实现它:到处使用通用类型。class SubY_generalist implements Y{    public Y f()     {        Y y = new SubY_generalist();         ...        return y;     } }使用特殊类型,但返回相同的值,隐式转换为通用类型。class SubY_mix implements Y{    public Y f()     {        SubY_mix y = new SubY_mix();         ...        return y;     } }使用特殊类型并返回相同的值。class SubY_specialist implements Y{    public SubY_specialist f()     {        SubY_specialist y = new SubY_specialist();         ...        return y;     } }我的考虑:这里附近有一个关于“接口编程”好处的长篇对话。最受推崇的答案似乎并没有深入探讨参数类型和返回类型之间的区别,它们实际上是根本不同的。因此,我发现其他地方的讨论并没有给我一个明确的答案,我别无选择,只能自己推测——当然,除非好心的读者能帮我一把。我将假设以下有关 Java 的基本事实:(它们正确吗?)一个物体是在其最特殊的时候被创建的。它可以随时隐式转换为更通用的类型(广义的) 。当一个对象被泛化时,它会失去一些有用的属性,但不会获得任何属性。从这些简单的观点来看,特殊物体比一般物体更有用,但也更危险。例如,我可能有一个可变容器,我可以将其概括为不可变。如果我确保容器在被冻结之前具有一些有用的属性,我可以在正确的时间对其进行概括,以防止用户意外破坏不变量。但这是正确的方法吗?还有另一种方法可以实现类似的隔离:我可能总是将我的方法设为package-private。泛化似乎更灵活,但很容易被忽略,并为微妙的错误引入一个表面。但在某些语言中,比如 Python,人们认为没有必要真正保护方法免受外部访问;他们只是用下划线标记内部方法。熟练的用户可以访问内部方法来获得一些收益,只要他们了解所有的复杂性。另一个结果是,在方法定义中我应该更喜欢专门的对象。 我的问题:这是正确的想法吗?我错过了什么吗?这与接口编程的讨论有何关系?这里的一些当地人似乎认为这是相关的,我也同意这一点,只是在我看来不是立即发生的。它更像是针对接口进行编程,或者通常针对子类进行编程。我对这些错综复杂的事情有点不知所措。
查看完整描述

1 回答

?
慕村9548890

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

所以,这是要点:

https://img1.sycdn.imooc.com/6528a5540001c27402760151.jpg

查看完整回答
反对 回复 2023-10-13
  • 1 回答
  • 0 关注
  • 71 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信