Java - 使用泛型或继承

afs*_*tos 5 java architecture generics 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> { ... }
Run Code Online (Sandbox Code Playgroud)

上面的两个实现在使用的锁定方案(常规锁或读写锁)方面有所不同.

还有ResourceManager一个负责管理这些事情的实体.我对客户端使用的想法是:

ResourceManager rm = ...
MyCustomObject o = ...
MyCustomReadWriteStrategy strat = ...
rm.newResourceFor(o, "id", strat);
Run Code Online (Sandbox Code Playgroud)

这样,客户端就会知道资源,但不必直接处理资源(因此包 - 私有类).此外,我可以自己实现一些常见的资源,如套接字,客户端只会要求它们(,我必须写一个SocketStrategy implements ResourceStrategy<Socket>).

ResourceManager rm = ...
rm.newSocketResource("id", host, port);
Run Code Online (Sandbox Code Playgroud)

要访问资源,他会向经理请求处理程序.这是由于每个线程都具有一些特定的访问权限,因此管理器将创建具有适当访问权限的处理程序.

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

这就是问题所在.
这种方法对我来说似乎很干净,对用户来说似乎也很直观.但是,正如所暗示的,管理者保持从标识符到资源的映射.如何声明,以及经理如何从地图中检索资源?

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.
Run Code Online (Sandbox Code Playgroud)

这种设计是否可以接受,和/或遵循良好做法?


或者,我可以消灭的仿制药,并有ExclusiveResourceShareableResource是抽象的和公开的.
这些类然后由我和客户端进行扩展,既为每一种类型的资源的需要(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);
}
Run Code Online (Sandbox Code Playgroud)

但是,继承方式似乎更正确.

afs*_*tos 0

正如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);
}
Run Code Online (Sandbox Code Playgroud)

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 { ... }
Run Code Online (Sandbox Code Playgroud)

资源现在对客户端公开。


结论

  1. 这两种方法都有可能滥用资源,绕过预期的契约和线程权限。这两种方法在这里是相同的。

  2. 使用泛型,客户端不需要知道资源的内部表示,因为管理器在后台创建资源。通过继承,资源创建发生在客户端,因此管理器的 API 将更改为接受提供的资源。

  3. 即使Resources 不是公开的,但使用泛型时,客户需要了解策略。通过继承,这些都消失了,public状态被分配给资源。

  4. 通过策略,可以在运行时更改行为,或者同一类型的资源可能有不同的行为。如果没有它们,客户端需要处置资源,然后使用另一个实现不同行为的子类重新创建它。
    例如:小文件可以完全读取到内存中,而大文件可能需要适当大小的缓冲区。

除非缺少其他内容,否则这可能只是一个选择问题,并考虑所需的 API 和用例。