[英]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.
這種設計是否可以接受,和/或遵循良好做法?
或者,我可以刪除泛型,並使ExclusiveResource
和ShareableResource
成為抽象和公共的。
然后,我和客戶端將為所需的每種類型的資源擴展這些類( FileResource extends ExclusiveResource
, SocketResource 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);
}
但是,繼承方式似乎更正確。
正如dkaustubh和Paul 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);
}
只有ResourceHandler
, ResourceManager
, ResourceState
和ResourceStrategy
需要公開給客戶端。
使用繼承,我可以通過一些權衡取得相同的結果。
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 { ... }
資源現在公開給客戶。
有兩種方法可以濫用資源,繞過預期的合同和線程權限。 這兩種方法都相同。
使用泛型,客戶端無需知道資源的內部表示,因為管理器在后台創建資源。 通過繼承,資源創建發生在客戶端,因此管理器的API將更改為接受提供的資源。
即使Resource
不是公共的,使用泛型,客戶端也需要了解策略。 通過繼承,這些已經消失, public
狀態被分配給資源。
使用策略,可以在運行時更改行為,或者對於相同類型的資源可能存在不同的行為。 沒有它們,客戶端需要處理資源,並使用另一個實現不同行為的子類重新創建它。
例如:小文件可以完全讀取到內存,而大文件可能需要適當大小的緩沖區。
除非缺少其他東西,否則它可能只是一個選擇問題,並考慮所需的API和用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.