簡體   English   中英

Java - 使用泛型或繼承

[英]Java - Using Generics or Inheritance

我有一個接口, Resource ,它應該包裝一些東西並在包裝對象上公開一些操作。
我的第一個方法是編寫以下內容,並考慮策略模式。

interface Resource<T> {
    ResourceState read();
    void write(ResourceState);
}

abstract class AbstractResource<T> implements Resource<T> {
    // This is where the Strategy comes in.
    protected AbstractResource(ResourceStrategy<T> strat) {
        // ...
    }

    // Both the read and write implementations delegate to the strategy.
}

class ExclusiveResource<T> extends AbstractResource<T> { ... }
class ShareableResource<T> extends AbstractResource<T> { ... }

上面的兩個實現在使用的鎖定方案(常規鎖或讀寫鎖)方面有所不同。

還有一個ResourceManager ,一個負責管理這些東西的實體。 我對客戶端使用的想法是:

ResourceManager rm = ...
MyCustomObject o = ...
MyCustomReadWriteStrategy strat = ...
rm.newResourceFor(o, "id", strat);

這樣,客戶端就會知道資源,但不必直接處理資源(因此包 - 私有類)。 此外,我可以自己實現一些公共資源,如套接字,客戶端只會要求它們( ,我必須編寫SocketStrategy implements ResourceStrategy<Socket> )。

ResourceManager rm = ...
rm.newSocketResource("id", host, port);

要訪問資源,他會向經理請求處理程序。 這是由於每個線程都具有一些特定的訪問權限,因此管理器將創建具有適當訪問權限的處理程序。

// This is in the ResourceManager class.
public ResourceHandler getHandlerFor(String id) {
    if (!canThreadUseThisResource(id)) throw ...;
    if (isThreadReaderOnly()) {
         return new ResourceReadHandler( ... );
    } else {
         return new ResourceWriteHandler( ... );
    }
}

這就是問題所在。
這種方法對我來說似乎干凈清晰,對用戶來說似乎也很直觀。 但是,正如所暗示的,管理者保持從標識符到資源的映射。 如何聲明,以及經理如何從地圖中檢索資源?

Map<String, Resource<?>> map;
// Can I go around without any specific cast? Not sure yet.
Resource<?> r = map.get(id);
// This could have an enum ResourceType, to check if thread has privileges
// for the specific type.

這種設計是否可以接受,和/或遵循良好做法?


或者,我可以刪除泛型,並使ExclusiveResourceShareableResource成為抽象和公共的。
然后,我和客戶端將為所需的每種類型的資源擴展這些類( FileResource extends ExclusiveResourceSocketResource extends ExclusiveResource ,...)。
這可能會消除對策略模式的需求,但會將更多的包暴露給用戶。

哪些替代方案最正確,或被廣泛接受為良好做法?


編輯:經過一番思考,我想我可以從Resource界面中刪除泛型,因為那是導致問題的那個,並將它留在AbstractResource及其子類上。 后者仍然可以授予我所用策略的編譯時驗證。

public <T> void newExclusiveResourceFor(
        T obj, String id, ResourceStrategy<T> strat) {
    ExclusiveResource<T> r = new ExclusiveResource<>(obj, strat);
    map.put(id, r);
}

但是,繼承方式似乎更正確。

正如dkaustubhPaul Bellora所建議的那樣Resource界面中的泛型沒有合理的理由。 起初,我完全沒有注意到這一點,因為我希望實現是通用的,所以我認為接口也應該是通用的。 事實並非如此。

我這里還有兩個選擇。

使用泛型

我應該刪除界面中的泛型。 然后,我最終得到以下內容。

interface Resource {
    ResourceState read();
    void write(ResourceState);
    void dispose();
}

abstract class AbstractResource<T> implements Resource {
    /* This is where the Strategy comes in.
     * The generic ensures compile-time verification of the
     * strategy's type. */
    protected AbstractResource(ResourceStrategy<T> strat) {
        // ...
    }

    // Both the read and write implementations delegate to the strategy.
}

class ExclusiveResource<T> extends AbstractResource<T> { ... }
class ShareableResource<T> extends AbstractResource<T> { ... }

// This is the behaviour the client implements, for custom resources.
public abstract class ResourceStrategy<T> {
    public abstract ResourceState read(T obj);
    public abstract void write(ResourceState state);
    public abstract void dispose(T obj);
}

只有ResourceHandlerResourceManagerResourceStateResourceStrategy需要公開給客戶端。


使用繼承

使用繼承,我可以通過一些權衡取得相同的結果。

public interface Resource {
    ResourceState read();
    void write(ResourceState);
    void dispose();
}

/* These implement only the locking schemes. */
abstract class ExclusiveResource implements Resource { ... }
abstract class ShareableResource implements Resource { ... }

/* The user extends these for custom content and behaviour. */
public abstract class CustomExclusiveResource
        extends ExclusiveResource { ... }
public abstract class CustomShareableResource
        extends ShareableResource { ... }

資源現在公開給客戶。


結論

  1. 有兩種方法可以濫用資源,繞過預期的合同和線程權限。 這兩種方法都相同。

  2. 使用泛型,客戶端無需知道資源的內部表示,因為管理器在后台創建資源。 通過繼承,資源創建發生在客戶端,因此管理器的API將更改為接受提供的資源。

  3. 即使Resource不是公共的,使用泛型,客戶端也需要了解策略。 通過繼承,這些已經消失, public狀態被分配給資源。

  4. 使用策略,可以在運行時更改行為,或者對於相同類型的資源可能存在不同的行為。 沒有它們,客戶端需要處理資源,並使用另一個實現不同行為的子類重新創建它。
    例如:小文件可以完全讀取到內存,而大文件可能需要適當大小的緩沖區。

除非缺少其他東西,否則它可能只是一個選擇問題,並考慮所需的API和用例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM