亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在方法的使用和定義之間靈活地保留有用的對象功能?

如何在方法的使用和定義之間靈活地保留有用的對象功能?

四季花海 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 關注
  • 96 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號