3

我正在寻找装饰器模式的替代方案,以使其更具活力。作为一个简单的示例,假设我们有以下代码:

interface Resource {
    public String getName();
}

interface Wrapper extends Resource {
    public Resource getSource();
}

interface Readable extends Resource {
    public InputStream getInputStream();
}

interface Listable extends Resource {
    public List<Resource> getChildren();
}



class File implements Readable {
    ...
}

class Zip implements Listable, Wrapper {
    public Zip(Readable source) { ... }
}

如您所见,Zip 并没有直接实现 Readable,但是它正在读取的资源却可以。假设我们构建一个 zip:

Zip zip = new Zip(new File());

我不想(也不能)堆叠所有接口以相互扩展(例如 Listable 扩展 Readable),我也不能构造所有对象来实现所有功能,因为并非所有功能都相互关联,你想要能够通过包装它们来动态“装饰”对象。

我确定这是一个常见问题,但有解决它的模式吗?使用“包装器”界面,您当然可以根据需要探测资源链以检查功能,但我不确定这是否是一种理智的方法。

更新

问题如上所述,并非所有功能都相关,因此您无法构建良好的接口层次结构。例如,假设您有这个任意的新功能:

interface Rateable extends Resource {
    public int getRating();
}

class DatabaseRateable implements Rateable, Wrapper {
    public DatabaseRateable(Resource resource) { ... }
}

如果你运行:

Resource resource = new DatabaseRateable(new Zip(new File));

生成的资源已经“丢失”了所有添加的功能(可读、可列出......)。让 Rateable 扩展说 Listable 是荒谬的。

我可以再次递归检查 resource.getSource() 并找出所有功能。在立即答复中没有明确的解决方案,所以也许递归检查毕竟是一个不错的选择?

4

6 回答 6

2

在我看来,您在这里努力追求的概念是鸭子类型,而 java 本身并没有这样做(请参阅我关于基于反射的 java 库的评论)。但是,在 JVM 上运行的其他语言当然可以。例如 -常规

class Duck {
    quack() { println "I am a Duck" }
}

class Frog {
    quack() { println "I am a Frog" }
}

quackers = [ new Duck(), new Frog() ]
for (q in quackers) {
    q.quack()
}

你可以用 groovy 编写你的代码,让它与你的 java 代码的其余部分无缝地一起工作,并在 groovy 中解决这个问题。

于 2013-03-27T08:47:39.827 回答
2

我认为您正在寻找的是混合

链接的维基百科页面有一个很好的支持它们的 OOP 语言列表。还是您特别与 Java 相关?

于 2013-03-27T08:49:51.597 回答
0

装饰对象时,通常只装饰对象的一个​​接口,仅修改或添加其行为的一个方面。您可以使用不同的装饰器装饰对象的另一个接口。这些装饰器可以同时存在。

您可以将一个装饰器传递给一个方法,另一个传递给另一个。

当您希望先用几个装饰器装饰对象然后通过代码传递对象时,这会变得很棘手。

因此,对于您的情况,我建议您再次将装饰器包装到一个对象中,该对象知道资源存在哪种装饰器。

class Resource {
    private Readable readable; 
    private Listable listable;
    private Rateable rateable;

    setReadable(Readable readable) {
        this.readable = readable;
    }

    setListable(Listable listable) {
        this.listable = listable;
    }

    setRateable(Rateable rateable) {
        this.rateable = rateable;
    }

    public boolean isRateable(){
        return rateable != null;
    }

    public Rateable getRateable(){
        return rateable;
    }
    // etc
}

File file1 = new File();
Resource resource = new Resource(file1);
resource.setReadable(new ReadableFile(file1));
resource.setListable(new ListableFile(file1));
resource.setRateable(new DatabaseRateableFile(file1));

然后,您可以传递资源,其用户可以发现该特定资源具有哪些功能。

Qi4j 框架允许您使用注释以更简洁的方式执行此操作(以及更多操作)。您将片段组合成组合。不过,这确实需要一些时间来适应。为 Resources 滚动您自己的特定实现的优点是它会更容易向其他人解释。

于 2013-03-27T08:47:43.060 回答
0

我将提供我自己的建议(如原始问题中所述)作为答案。如果有足够多的人认为这是一个有价值的解决方案或没有更好的解决方案,我会接受。

简而言之,我自己的解决方案包括使用 Wrapper 接口向后遍历资源以确定存在哪些功能。举个例子:

Resource resource = new DatabaseRateable(new Zip(new File));

你可以想象这样做:

public Readable asReadable(Resource resource) {
    if (resource instanceof Readable)
        return (Readable) resource;
    else if (resource instanceof Wrapper)
        return (asReadable( ((Wrapper) resource).getSource() );
    else
        return null;
}
于 2013-03-27T10:02:44.833 回答
0

也许适配器模式可以提供帮助:

Readable r = zip.adapt( Readable.class );

这要求方法adapt()返回一个zip实现Readable接口的实例。

实现通常使用“适配器管理器”,它知道如何为所有注册类型构建包装器。

于 2013-03-27T08:50:20.703 回答
0

这里可能不太合适,但动态特征发现:

public class Features {

    public <T> lookup(Class<T> intface) 
            throws UnsupportedOperationException {
        return lookup(intface, intface.getSimpleName());
    }

    public <T> lookup(Class<T> intface, String name) 
            throws UnsupportedOperationException {
        return map.get(...);
    }
}

public class X {
    public final Features FEATURES = new Features();
    ...
}

X x;
Readable r = x.FEATURES.lookup(Readable.class);
于 2013-03-27T08:55:20.340 回答