PHP에서 Kotlin으로 - 6일차 - 엔터티

엔티티는 다른 것입니다. 더 복잡하고 더 많은 주의가 필요합니다.


명세서


머쉬에는 고유 식별자가 있습니다.

값 개체가 있어야 합니다.

다른 엔터티를 포함할 수 있어야 합니다.

필요한 경우 "명명된 생성자"를 다른 방식으로 구성할 수 있어야 합니다.


이 경우 우리는 모어 엔티티를 생성할 것입니다.

The truth is that before starting this activity, the Mower entity was de first that was created, but it was quite anemic, since it needed other value objects like position or orientation to be created by composition and that's why I left it parked, since that didn't serve our purpose.



모어에서 필요한 것은 다음과 같습니다.
  • 빌드 가능
  • 좌회전 가능
  • 우회전 가능
  • 움직일 수 있는 것
  • 지도의 한계를 벗어났는지 감지합니다.

  • 다음과 같은 테스트를 남깁니다.

    private const val MOVE_LEFT: String = "L"
    private const val MOVE_RIGHT: String = "R"
    private const val MOVE_FORWARD: String = "F"
    
    private const val X_POSITION: Int = 1
    private const val Y_POSITION: Int = 1
    private const val STEP: Int = 1
    
    private const val NORTH_ORIENTATION: String = "N"
    private const val WEST_ORIENTATION: String = "W"
    private const val EAST_ORIENTATION: String = "E"
    private const val SOUTH_ORIENTATION: String = "S"
    
    internal class MowerTest {
        private lateinit var mower: Mower
        private val mowerId: MowerId = MowerId.build(UUID.randomUUID().toString())
        private lateinit var mowerPosition: MowerPosition
    
        @BeforeEach
        fun setUp() {
            mowerPosition = MowerPosition.build(
                XMowerPosition.build(X_POSITION),
                YMowerPosition.build(Y_POSITION),
                MowerOrientation.build(NORTH_ORIENTATION)
            )
    
            mower = Mower.build(mowerId, mowerPosition)
        }
    
        @Test
        fun `Should be build`() {
            val mower = Mower.build(mowerId, mowerPosition)
    
            assertThat(mower).isInstanceOf(Mower::class.java)
            assertThat(mower.mowerId).isEqualTo(mowerId)
            assertThat(mower.mowerPosition()).isEqualTo(mowerPosition)
        }
    
        @Test
        fun `Should be able to turn left`() {
            val expectedMowerOrientation = MowerOrientation.build(WEST_ORIENTATION)
    
            mower.move(MowerMovement.build(MOVE_LEFT))
            assertThat(mower.mowerPosition().orientation).isEqualTo(expectedMowerOrientation)
        }
    
        @Test
        fun `Should be able to turn right`() {
            val expectedMowerOrientation = MowerOrientation.build(EAST_ORIENTATION)
    
            mower.move(MowerMovement.build(MOVE_RIGHT))
            assertThat(mower.mowerPosition().orientation).isEqualTo(expectedMowerOrientation)
        }
    
        @Test
        fun `Should be able to move forward`() {
            mower.move(MowerMovement.build(MOVE_FORWARD))
            assertThat(mower.mowerPosition().xPosition.value).isEqualTo(X_POSITION)
            assertThat(mower.mowerPosition().yPosition.value).isEqualTo(Y_POSITION + STEP)
        }
    
        @ParameterizedTest
        @MethodSource("positionAndMovementProvider")
        fun `Should throw exception if goes out of bounds`(xPosition: Int, yPosition: Int, orientation: String) {
            val mowerPosition = MowerPosition.build(
                XMowerPosition.build(xPosition),
                YMowerPosition.build(yPosition),
                MowerOrientation.build(orientation)
            )
    
            val mower = Mower.build(mowerId, mowerPosition)
    
            assertThrows(InvalidMowerPositionException::class.java) {
                mower.move(MowerMovement.build(MOVE_FORWARD))
            }
        }
    
        companion object {
            @JvmStatic
            fun positionAndMovementProvider(): Stream<Arguments> {
                return Stream.of(
                    Arguments.arguments(0, 0, SOUTH_ORIENTATION),
                    Arguments.arguments(0, 0, WEST_ORIENTATION),
                    Arguments.arguments(0, 5, NORTH_ORIENTATION),
                    Arguments.arguments(5, 0, EAST_ORIENTATION)
                )
            }
        }
    }
    


    이전 값 개체에서 모든 것이 잘 되었다면 모어는 다음과 같아야 합니다.

    import mower.mower.value_object.MowerId
    import mower.mower.value_object.MowerMovement
    import mower.mower.value_object.MowerPosition
    
    class Mower private constructor(val mowerId: MowerId, private var mowerPosition: MowerPosition) {
    
        fun move(movement: MowerMovement) {
            mowerPosition = mowerPosition.move(movement)
        }
    
        companion object {
            @JvmStatic
            fun build(mowerId: MowerId, mowerPosition: MowerPosition): Mower {
                return Mower(mowerId, mowerPosition)
            }
        }
    
        fun mowerPosition(): MowerPosition {
            return mowerPosition
        }
    }
    


    모든 논리는 자연스럽게 가치 개체에 배포되었습니다. 모든 조각은 기능을 수행합니다. Te Mower Sould는 전달한 움직임만 MourePosition 개체에 적용합니다. 이 개체는 앞으로 이동인지 방향 변경인지를 결정하고 필요한 변경을 실행합니다...

    Value 개체 XMowerPosition 및 YMowerPosition은 좌표의 음수 위치를 감시합니다. 그러나 양의 좌표에서 지도 외부에 있는지 알 수 없습니다. 모어용으로 만든 테스트는 이 특정 필드에서 실패하므로 해당 캐슈스트리를 검증하기 위해 맵을 전달해야 합니다...


    보너스 테스트 및 컨텍스트



    사실 저는 테스트가 실행될 때 약간의 설명을 하는 것을 매우 좋아합니다. 우리는 언제 실패할지 알 수 없으며 우리 뒤에 오는 사람은 왜 실패하는지에 대한 약간의 맥락이 있습니다.
    사용된 값에 대한 컨텍스트가 없는 테스트는 어려울 수 있습니다.

    ...
        @ParameterizedTest(name = "{0}")
        @MethodSource("positionAndMovementProvider")
        fun `Should throw exception if goes out of bounds`(scenario: String, xPosition: Int, yPosition: Int, orientation: String) {
    ...
        }
    
        companion object {
            @JvmStatic
            fun positionAndMovementProvider(): Stream<Arguments> {
                return Stream.of(
                    Arguments.arguments("Out of bounds by negative value movement axis Y", 5, 0, SOUTH_ORIENTATION),
                    Arguments.arguments("Out of bounds by negative value movement axis X", 0, 5, WEST_ORIENTATION),
                    Arguments.arguments("Out of bounds movement axis Y", 0, 5, NORTH_ORIENTATION),
                    Arguments.arguments("Out of bounds movement axis X", 5, 0, EAST_ORIENTATION)
                )
            }
        }
    


    테스트에서 사용되지 않을 인수를 추가하는 것이 약간 이상하게 보일 수 있습니다.... 하지만 그럴 때 의미가 있습니다.

    우리는 이것에서 출발합니다:



    아니면 이거:



    이에:



    훨씬 더 명확하고 읽기 쉽습니다 ...

    JUnit이 실제로 변수를 사용하지 않는다는 경고를 표시하지만... 우리가 사용하는 것을 좋아하지 않는 것 같습니다.




    링크 및 리소스



    Repository Mower Kata

    좋은 웹페이지 즐겨찾기