桥设计模式导致泛型过多

时间:2017-06-16 00:40:09

标签: java generics kotlin bridge

我正在为Minecraft做一些事情,我正在使用桥接模式,所以我的代码可以用于两个独立的服务器平台,Sponge和Bukkit,具有不同(但有些类似)的API。

我有我的核心代码,它仅依赖于我后来需要的Minecraft中的常见事物的抽象,例如玩家和物品,以及抽象工厂和构建器类。特定服务器平台的代码将实现工厂和构建器,并将它们作为核心代码的依赖项提供。

到目前为止,这种方法运作良好,但我遇到的问题是抽象相互依赖。例如,我有适用于Minecraft库存的适配器和适应服务器平台项目/库存类型的项目,以及我自己的抽象项目/库存类型。项目和库存对象需要相互交互,并且由于核心代码不了解实现,我已经使用了泛型。这是一些伪代码:

interface Item<TBase> {
    TBase getBase();
}
interface Inventory<TItem extends Item<*>> {
    void addItem(TItem item);
}

Item类根据服务器平台使用的项类型调整项目。 addItem()方法实现将使用getBase()方法将服务器平台项的实例添加到服务器平台库存的实例中。总的来说,泛型提供了平台特定对象之间交互的解决方案。

然而,我遇到的问题是,随着项目规模的增加,泛型变得越来越复杂。一个原因是使用物品/库存的类需要相同的泛型。例如,所有玩家都有一个库存:

interface Player<TItem extends Item<*>> {
    Inventory<TItem> getInventory();
    void giveItem(TItem item);
}

使用播放器的东西需要有泛型,等等。

第二个问题是实例之间的交互比这两个更多,这可能意味着一个对象的几个泛型参数,因此在使用该对象的所有类上有更多的泛型。

我认为另一种解决方案根本就是不使用泛型,更改getBase()以返回Object类型,并盲目地投射,相信它是正确的类型(它将是)。

我对此深思熟虑,这是我能想到的最好的。我想知道是否还有其他任何我缺失的解决方案,或者任何可能有助于解决此问题的设计模式。

如果有源有帮助,你可以在这里找到它: https://github.com/BenWoodworth/FastCraft/tree/master/src/main/kotlin/net/benwoodworth/fastcraft

2 个答案:

答案 0 :(得分:0)

编辑:嗯,这不是一种桥梁模式吗?

public interface InventoryHolder
{
    public void transfer(Player toPlayer, Item item);
}

然后

public class Player implements InventoryHolder
{
    List<Item> items;
    public Item removeItem(Item item){
        return items.remove(items.indexOf(item));
    }
    public void addItem(Item item) {
        items.add(item);
    }
    public void transfer(Player toPlayer, Item item)
    {
        toPlayer.addItem(removeItem(item));
    }
}

public class Item {}

所以

public class PlayGame
{
    public static void main(String... args) {
        new PlayGame().run();
    }
    private void run() {
        Player p1 = new Player();
        Player p2 = new Player();
        Item item = new Item();
        p1.addItem(item);
        // transfer
        p1.transfer(p2, item);
    }
}

答案 1 :(得分:0)

这是我目前的解决方案。如果您有任何改进的余地,请务必分享您的见解。这是我的一些源代码,简化并用Kotlin编写。

依赖关系:

// An abstract class, to be used by objects adapting
// native implementations. Provides an unwrap method,
// to unwrap the adapted object.
abstract class Adapter(protected val base: Any) {
    @Suppress("UNCHECKED_CAST")
    fun <T> unwrap() = base as T
}

// Inventory adapter, extends Adapter
abstract class InventoryAdapter(baseInventory: Any) : Adapter(baseInventory)

// Player adapter, extends Adapter
abstract class PlayerAdapter(basePlayer: Any) : Adapter(basePlayer) {
    abstract fun openInventory(inventory: InventoryAdapter)
}

海绵实施:

// Adapts Player (from the Sponge API)
class SpongePlayerAdapter(
        protected val basePlayer: Player
): PlayerAdapter(basePlayer) {

    override fun openInventory(inventory: InventoryAdapter) {
        // The unwrap<T>() method inferences T as Sponge's
        // Inventory type, from the openInventory parameter
        basePlayer.openInventory(inventory.unwrap())
    }
}

以类型安全为代价,已经消除了对仿制药的需求。可以通过传入PlayerAdapter.openInventory()对象作为参数来调用InventoryAdapter。如果PlayerAdapterSpongePlayerAdapter,且InventoryAdapterSpongeInventoryAdapter,则unwrap()方法将返回海绵Inventory,将按预期为玩家打开库存。

例如,如果传入了BukkitInventoryAdapter个对象,则会在运行时抛出一个强制转换异常,因为unwrap()方法会尝试将Bukkit Inventory强制转换为Sponge { {1}}。这不是一个大问题,只要正确注入依赖项,就不会导致错误。