桥梁设计模式造成了太多的泛型
我目前正在为Minecraft开发一些东西,而且我正在使用桥接模式,所以我的代码可以和两个独立的服务器平台(Sponge和Bukkit)一起使用,但是它们有不同的API(但有点相似)。
我有我的核心代码,这只取决于我后面需要的Minecraft中常见的东西的抽象,比如玩家和物品,以及抽象工厂和建造者类。 特定服务器平台的代码将实现工厂和构建器,并将它们作为依赖关系提供给核心代码。
到目前为止,这一直工作得很好,但我遇到了一个相互依赖的抽象问题。 例如,我有适用于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
编辑:那么,这不是一个桥梁模式?
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); } }
这是我目前的解决方案。 如果您看到有任何改进的余地,请分享您的见解。 这里有一些我的源代码,简化了,写在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
对象作为参数来调用。 如果PlayerAdapter
是一个SpongePlayerAdapter
,并且InventoryAdapter
是一个SpongeInventoryAdapter
,那么SpongeInventoryAdapter
unwrap()
方法将返回一个海绵Inventory
,并按照预期为玩家打开库存。
例如,如果BukkitInventoryAdapter
对象,则会在运行时抛出一个转换异常,因为unwrap unwrap()
方法将尝试将Bukkit Inventory
转换为海绵Inventory
。 这不是一个大问题,只要依赖注入正确,不应该导致错误。