2017-06-16 61 views
1

我目前工作的东西我的世界,而我使用桥接模式,所以我的代码可以使用两个独立的服务器平台,海绵和Bukkit,不同(但有点类似于使用)API的。造成太多的仿制药大桥设计模式

我有我的核心代码,这仅取决于在我的世界共同的东西,我稍后需要的,比如玩家和物品,以及抽象工厂和制造商类抽象。特定服务器平台的代码将实现工厂和构建器,并将它们作为依赖关系提供给核心代码。

这一直工作得很好,到目前为止,但我遇到了一个问题,与互相依赖抽象。例如,我有适用于Minecraft库存的适配器和适用于我自己的抽象项目/库存类型的项目/库存类型的项目。 Item和Inventory对象需要互相交互,并且由于核心代码不知道实现,所以我使用泛型。这里有一些伪代码:

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

+0

为什么'Player'需要与'Item'有关的通用类? '玩家'不是'物品'。 –

+0

我不确定如何确保'getInventory()'提供了一个'Inventory',其中包含'Item'的正确实现。 –

回答

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); 
    } 
} 
+0

我有'SpongeItemBuilder'和'BukkitItemBuilder'实现的抽象依赖项'ItemBuilder',用于为各个平台(而不是'new Item()')构建项目。假设玩家的“库存”是BukkitInventory的具体实现,它调整** Bukkit **库存对象。如果我使用'SpongeItemBuilder'构建一个'Item',没有类型安全阻止我试图将'Item'(适应** Sponge **项)添加到** Bukkit **清单。来自正在改编的Bukkit API的广告资源无法接受来自** Sponge ** API的项目。 –

+0

老实说这个问题是一个设计'意见'的问题,不属于SO。我会鼓励你退后一步并着手“2.0版”。一个人是一个对象,所以我认为这不是一般意义。它可能是一个基类,但泛型类往往会对事物起作用,就像列表存储事物一样。所以,你有一点误入歧途,需要重新启动。 –

+0

我的想法是,抽象玩家携带一个泛型类型的项目,因为列表包含泛型类型的对象。总的来说,我在问,因为我想看看是否有一种标准的方法来桥接API与这样的交互,这样我就可以用一种更好的方法来反思我的不可思议。 2.0版本的种类。 –

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()) 
    } 
} 

需要仿制药已被删除,在类型安全的成本。可以通过传入InventoryAdapter对象作为参数来调用PlayerAdapter.openInventory()。如果PlayerAdapterSpongePlayerAdapter,并且InventoryAdapterSpongeInventoryAdapter,则该方法将返回海绵Inventory,并且如预期地为玩家打开库存。

如果BukkitInventoryAdapter对象进行传递,例如铸造异常会在运行时被抛出,因为unwrap()方法将尝试施放Bukkit Inventory到海绵Inventory。这不是一个大问题,只要依赖关系被正确注入就不应该导致错误。