Java-使用泛型或继承 - java

我有一个接口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);
}

但是,遵循继承方式似乎更正确。

参考方案

正如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);
}

对客户端来说,只有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 { ... }

资源现在对客户公开。

结论

  • 有两种方法都可以滥用资源,从而绕过预期的合同和线程权限。两种方法在这里是相等的。
  • 使用泛型,客户端不需要知道资源的内部表示形式,因为管理器在后台创建资源。通过继承,资源创建在客户端进行,因此管理器的API将更改为接受提供的资源。
  • 即使Resource不公开,使用泛型,客户端也需要了解策略。有了继承,它们就消失了,并将public状态分配给资源。
  • 使用策略,可以在运行时更改行为,或者对于相同类型的资源可以有不同的行为。没有它们,客户端需要处理资源,然后他们使用实现不同行为的另一个子类重新创建资源。
    例如:小文件可以完全读取到内存,而大文件可能需要适当大小的缓冲区。
  • 除非缺少其他内容,否则可能只是选择问题,并考虑所需的API和用例。

    Java中的<<或>>>是什么意思? - java

    This question already has answers here: Closed 7 years ago. Possible Duplicate: What does >> and >>> mean in Java?我在一些Java代码中遇到了一些陌生的符号,尽管代码可以正确编译和运行,但对于括号在此代码中的作用却感…

    菱形运算符<>是否等于<?> - java

    我在util.TreeSet类中发现,其中一个构造函数正在使用具有空泛型类型的新TreeMap调用另一个构造函数。 public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); } new TreeMap<>是什么意思…

    与哪些运算符>>兼容 - java

    我这里没有什么代码int b=3; b=b >> 1; System.out.println(b); 它可以完美工作,但是当我将变量b更改为byte,short,float,double时,它包含错误,但是对于变量int和long来说,它可以完美工作,为什么它不能与其他变量一起工作? 参考方案 位移位运算符(例如>>)与任何整数类型兼…

    休眠映射<键,设置<值>> - java

    我有以下表格:@Entity @Table(name = "events") Event --id --name @Entity @Table(name = "state") State --id --name @Entity @Table(name = "action") Action --id …

    Java中的“ <<”运算符 - java

    最喜欢的语句来自Java的Character类:(1 << Character.PARAGRAPH_SEPARATOR)) >> type PARAGRAPH_SEPARATOR是字节,type是整数。这句话中的操作员,他们做什么?如何以及在哪里可以使用这些运算符?这是oracles java.lang.Character文档。该类中…