避免使用原始类型警告,使用一个工厂返回实现策略模式的通用对象。

huangapple go评论69阅读模式
英文:

Avoid raw type warnings with a factory that returns a generic object implementing a strategy pattern

问题

我的目标是拥有一个抽象的 Game 类,其构造函数接受适用于派生的 Game 类的 GameEngine。这个抽象的 Game 类将实现适用于所有 Game 实现的通用方法。你可以将 GameEngine 视为一个策略模式,Game 类将方法委托给它。

因此,在使用 GameFactory 创建一个 Game 时,我不关心工厂返回的 Game 实现是什么。我只想确保 Game 实现使用适当的 GameEngine 实现进行构造。然而,如果我简单地从工厂返回原始类型 Game,当然会得到 Raw use of parameterized class 'Game' 的警告。

此外,理想情况下,GameFactory.createGame(settings) 方法不应该传递类型,而是根据 settings 的某个属性来推断类型。

以下是代码的主要部分:

public abstract class GameEngine<T extends Game<T>> {
  public void doSomethingAllEnginesUnderstand(T game);
}

public class LocalGameEngine extends GameEngine<LocalGame> {
}

public class RemoteGameEngine extends GameEngine<RemoteGame> {
}

public abstract class Game<T extends Game<T>> {
  private final GameEngine<T> gameEngine;
  protected Game(GameEngine<T> gameEngine) {
    this.gameEngine = gameEngine;
  }
  protected abstract T getSelf();
  public final void doSomethingAllEnginesUnderstand() {
    gameEngine.doSomethingAllEnginesUnderstand(getSelf());
  }
}

public class LocalGame extends Game<LocalGame> {
  public LocalGame(LocalGameEngine gameEngine) {
    super(gameEngine);
  }
  @Override
  protected LocalGame getSelf() {
    return this;
  }
}

public class RemoteGame extends Game<RemoteGame> {
  public RemoteGame(RemoteGameEngine gameEngine) {
    super(gameEngine);
  }
  @Override
  protected RemoteGame getSelf() {
    return this;
  }
}

public class GameFactory {
  public Game createGame(GameSettings settings) {
    if (settings.totalPlayers() > 1) {
      return new RemoteGame(new RemoteGameEngine());
    } else {
      return new LocalGame(new LocalGameEngine());
    }
  }
}

我是否误用/误解了泛型来实现我的目标?是否可能在仍然要求将适当的 GameEngine 实现传递给构造函数的情况下,将 Game 设置为非泛型类?

英文:

My goal is to have an abstract Game class, whose constructor accepts a GameEngine that is suitable for derived Game classes. This abstract Game class will implement generic methods that are suitable for all Game implementations. You can think of the GameEngine as fulfilling a strategy pattern, to which Game delegates methods.

Therefore, upon creating a Game with a GameFactory, I don't care what implementation of Game the factory returns. I just want to make sure that the Game implementation gets constructed with the appropriate GameEngine implementation. However, if I simply return the raw type Game from the factory, I of course get Raw use of parameterized class &#39;Game&#39; warnings.

Furthermore, ideally, the GameFactory.createGame(settings) method should not have to pass a type, but simply infer the type based on some property of settings.

This is the gist of the code I have:

public abstract class GameEngine&lt;T extends Game&lt;T&gt;&gt; {

  public void doSomethingAllEnginesUnderstand(T game);
}

public class LocalGameEngine
    extends GameEngine&lt;LocalGame&gt; {
}

public class RemoteGameEngine
    extends GameEngine&lt;RemoteGame&gt; {
}

public abstract class Game&lt;T extends Game&lt;T&gt;&gt; {

    private final GameEngine&lt;T&gt; gameEngine;

    protected Game(GameEngine&lt;T&gt; gameEngine) {
        this.gameEngine = gameEngine;
    }

    protected abstract T getSelf();
    
    public final void doSomethingAllEnginesUnderstand() {
      gameEngine.doSomethingAllEnginesUnderstand(getSelf());
    }
}

public class LocalGame 
    extends Game&lt;LocalGame&gt; {

    public LocalGame(LocalGameEngine gameEngine) {
        super(gameEngine);
    }

    @Override
    protected LocalGame getSelf() {
        return this;
    }
}

public class RemoteGame 
    extends Game&lt;RemoteGame&gt; {

    public RemoteGame(RemoteGameEngine gameEngine) {
        super(gameEngine);
    }

    @Override
    protected RemoteGame getSelf() {
        return this;
    }
}

public class GameFactory {

  // returns raw type Game
  public Game createGame(GameSettings settings) {
    if(settings.totalPlayers() &gt; 1) {
      return new RemoteGame(new RemoteGameEngine());
    }
    else {
      return new LocalGame(new LocalGameEngine());
    }
  }
}

Am I misusing/misunderstanding generics to reach my stated goal? Is it possible to not make Game a generics class, while still mandating that an appropriate GameEngine implementation is passed to the constructor?

答案1

得分: 1

我认为您过多地使用了泛型,并且我认为Game&lt;T extends Game&lt;T&gt;&gt;有些过于繁琐。我会按照以下方式进行设计:

  • LocalGameRemoteGame都继承自Game(无泛型类型)。在您重写getSelf方法时使用多态:
public class LocalGame extends Game {

    public LocalGame(LocalGameEngine gameEngine) {
        super(gameEngine);
    }

    @Override
    protected LocalGame getSelf() {
        return this;
    }
}
public class RemoteGame extends Game {

    public RemoteGame(RemoteGameEngine gameEngine) {
        super(gameEngine);
    }

    @Override
    protected RemoteGame getSelf() {
        return this;
    }
}
  • LocalGameEngineRemoteGameEngine都继承自GameEngine(无泛型类型):
// 实际上,这个抽象类可以是一个接口
public abstract class GameEngine {
    public void doSomethingAllEnginesUnderstand(Game game) {}
}
public class LocalGameEngine extends GameEngine { }
public class RemoteGameEngine extends GameEngine { }
  • Game要求任何GameEngine的子类,泛型部分只需在构造函数中使用,无需将整个Game定义为泛型(Game&lt;T&gt;):
public abstract static class Game {

    private final GameEngine gameEngine;

    protected <T extends GameEngine> Game(T gameEngine) {
        this.gameEngine = gameEngine;
    }

    protected abstract Game getSelf();

    public final void doSomethingAllEnginesUnderstand() {
        gameEngine.doSomethingAllEnginesUnderstand(getSelf());
    }
}
  • 整个GameFactory变得更简化:
public Game createGame(GameSettings settings) {
    if (settings.totalPlayers() > 1) {
        return new RemoteGame(new RemoteGameEngine());
    } else {
        return new LocalGame(new LocalGameEngine());
    }
}
英文:

I feel you overuse the generics and I find Game&lt;T extends Game&lt;T&gt;&gt; an overkill. I'd design it in this way:

  • Make LocalGame and RemoteGame extends Game (no generic type). Use polymorphysm when you @Override the getSelf method:

    public class LocalGame extends Game {
    public LocalGame(LocalGameEngine gameEngine) {
    super(gameEngine);
    }
    @Override
    protected LocalGame getSelf() {
    return this;
    }
    }
    
    public class RemoteGame extends Game {
    public RemoteGame(RemoteGameEngine gameEngine) {
    super(gameEngine);
    }
    @Override
    protected RemoteGame getSelf() {
    return this;
    }
    }
    
  • Make LocalGameEngine and RemoteGameEngine extends GameEngine (no generic type)

    // actuyally, this abstract class can be an interface instead
    public abstract class GameEngine {
    public void doSomethingAllEnginesUnderstand(Game game) {}
    }
    
    public class LocalGameEngine extends GameEngine { }
    
    public class RemoteGameEngine extends GameEngine { }
    
  • Make Game require any subclass of GameEngine, the generic part can be used in the constructor only, you don't need to make the whole Game generic (Game&lt;T&gt;).

    public abstract static class Game {
    private final GameEngine gameEngine;
    protected &lt;T extends GameEngine&gt; Game(T gameEngine) {
    this.gameEngine = gameEngine;
    }
    protected abstract Game getSelf();
    public final void doSomethingAllEnginesUnderstand() {
    gameEngine.doSomethingAllEnginesUnderstand(getSelf());
    }
    }
    
  • The whole GameFactory becomes simplified:

    public Game createGame(GameSettings settings) {
    if (settings.totalPlayers() &gt; 1) {
    return new RemoteGame(new RemoteGameEngine());
    } else {
    return new LocalGame(new LocalGameEngine());
    }
    }
    

huangapple
  • 本文由 发表于 2020年10月25日 13:42:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/64520721.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定