介绍目前,我有某种形式的东西:Tetris class ---> FallingPiece class ----> Piece classPiece可以是Square,T等。它具有有关其形状,旋转形状,大小等信息。FallingPiece类基本上包含一个引用Piece(俄罗斯方块游戏中当前下降的棋子)的属性,并且可能具有其当前(x, y)位置和颜色。我最初的设计是采用以下形式:class Tetris { private IPieceGenerator pieceGenerator; private FallingPiece fallingPiece; ... public Tetris(IPieceGenerator pieceGenerator) { this.pieceGenerator = pieceGenerator; ... } private void someMethodThatNeedsAFallingPiece() { if (fallingPiece == null) { Piece piece = pieceGenerator.Generate(); fallingPiece = new FallingPiece(piece); } ... }}哪个当然有问题,如果我以后要对我的Tetris类进行单元测试,并想知道我当前的(x, y)在板的FallingPiece位置中,我就不会。我记得曾经在神话般的Misko Hevery's The Clean Code Talks中看到了这个“问题”。第一个问题似乎是由于我要赋予Tetris类创建FallingPiece对象的责任,因此我无法对其进行引用(我通过构造函数注入将Piece工厂而不是FallingPiece厂!)。现在,我可以看到至少两种解决方法:我可以为internal属性定义一个package-protected(C#)/ FallingPiece(Java)吸气剂,因此我可以轻松对其进行测试。这看似无害,但我觉得它并不优雅。我可以代替传递Piece工厂,而传递FallingPiece工厂。然后,我可以控制Factory将返回哪些对象并通过它访问它们。你们怎么看?常用吗?还有其他解决方法吗?还有第二个问题与以下事实有关:我最初将FallingPiece实现为不可变类型。举例来说,这意味着每次我想更新FallingPiece在Tetris板上的位置时,我都必须创建一个新的FallingPiece实例,而Tetris上的属性现在将指向一个新的。例如,如果我希望通过传递到FallingPiece类的FallingPiece访问FallingPieceFactory引用,这可能是一个大问题。在我看来,如果在尝试测试类时误用了很多麻烦,那么不变的数据类型就会成为现实,对吧?还是这首先是对不可变数据类型的错误使用?谢谢 (adsbygoogle = window.adsbygoogle || []).push({}); 最佳答案 Possiblle解决方案:不用将实现连接在一起,而是使用接口。引入一个生成FallingPiece的生成器这样,您可以使用接口的模拟对象来测试所有内容。将someMethodThatNeedsAFallingPiece的可见性更改为受保护以在测试用例中访问它,或使用反射(邪恶)调用它。public class Tetris { private IPieceGenerator pieceGenerator; private IFallingPiece fallingPiece; private IFallingPieceGenerator fallingPieceGenerator; public Tetris(IPieceGenerator pieceGenerator, IFallingPieceGenerator fallingPieceGenerator) { this.pieceGenerator = pieceGenerator; this.fallingPieceGenerator = fallingPieceGenerator; } protected void someMethodThatNeedsAFallingPiece() { if (fallingPiece == null) { IPiece piece = pieceGenerator.Generate(); fallingPiece = fallingPieceGenerator.generate(piece); } }}这是您的单元测试:public class TetrisTest { private IPieceGenerator pieceGeneratorMock; private IFallingPieceGenerator fallingPieceGeneratorMock; private IFallingPiece fallingPieceMock; private IPiece pieceMock; private Tetris tetris; @Before public void init() { this.pieceGeneratorMock = EasyMock.createMock(IPieceGenerator.class); this.fallingPieceGeneratorMock = EasyMock.createMock(IFallingPieceGenerator.class); this.fallingPieceMock = EasyMock.createMock(IFallingPiece.class); this.pieceMock = EasyMock.createMock(IPiece.class); this.tetris = new Tetris(pieceGeneratorMock, fallingPieceGeneratorMock); } @Test public void testSomeMethodThatNeedsAFallingPiece() { expect(pieceGeneratorMock.Generate()).andReturn(pieceMock); expect(fallingPieceGeneratorMock.generate(pieceMock)).andReturn(fallingPieceMock); replay(fallingPieceMock, fallingPieceGeneratorMock, pieceGeneratorMock, pieceMock); tetris.someMethodThatNeedsAFallingPiece(); verify(fallingPieceMock, fallingPieceGeneratorMock, pieceGeneratorMock, pieceMock); }} (adsbygoogle = window.adsbygoogle || []).push({}); 09-08 04:19