我正在为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
答案 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
。如果PlayerAdapter
为SpongePlayerAdapter
,且InventoryAdapter
为SpongeInventoryAdapter
,则unwrap()
方法将返回海绵Inventory
,将按预期为玩家打开库存。
例如,如果传入了BukkitInventoryAdapter
个对象,则会在运行时抛出一个强制转换异常,因为unwrap()
方法会尝试将Bukkit Inventory
强制转换为Sponge { {1}}。这不是一个大问题,只要正确注入依赖项,就不会导致错误。