티스토리 뷰

Android/Kotlin

[Kotlin]  Tic-tac-toe 게임으로 보는 MVP 패턴

혀가 길지 않은 개발자 2020. 9. 18. 23:10

MVP (Model  +  View  +  Presenter)


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="30dp"
    tools:context=".view.MainActivity">

    <GridLayout
        android:id="@+id/btnGrid"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:rowCount="3"
        android:columnCount="3">

        <Button
            android:tag="00"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="01"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="02"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="10"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="11"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="12"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="20"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="21"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

        <Button
            android:tag="22"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:onClick="onClickCell"
            android:textSize="30sp"/>

    </GridLayout>

    <LinearLayout
        android:id="@+id/winnerPlayerViewGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone"
        tools:visibility="visible">

        <TextView
            android:id="@+id/tvWinnerPlayer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="40sp"
            android:layout_margin="20dp"
            tools:text="X"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Winner"
            android:textSize="40sp"/>

    </LinearLayout>

    <Button
        android:id="@+id/btnReset"
        android:layout_width="200dp"
        android:layout_height="70dp"
        android:layout_marginTop="40dp"
        android:textAllCaps="false"
        android:text="Reset"
        android:textSize="20dp"/>

</LinearLayout>

activity_main.xml

 

 

 

 

 

model/Player.kt

package com.jwsoft.kotlinproject.model

enum class Player {
    O,
    X,
    NULL
}

 

 

 

 

 

model/Cell.kt

package com.jwsoft.kotlinproject.model

data class Cell (
    var player: Player = Player.NULL
)

 

 

 

 

 

model/Board.kt

package com.jwsoft.kotlinproject.model

class Board {

    lateinit var currentPlayer: Player        // X 선차례
    lateinit var cells: Array<Array<Cell>>

    init {
        clearCells()
    }

    fun mark(row: Int, col: Int): Player {
        if (isValid(row, col)) {
            cells[row][col].player = currentPlayer
            return currentPlayer
        } else {
            return Player.NULL
        }
    }

    fun isValid(row: Int, col: Int): Boolean {
        return cells[row][col].player == Player.NULL
    }

    fun flipPlayer() {
        if (currentPlayer == Player.X) currentPlayer = Player.O
        else currentPlayer = Player.X
    }

    fun getWinner(row: Int, col: Int, player: Player): Player {
        if (cells[row][0].player == player &&
            cells[row][1].player == player &&
            cells[row][2].player == player ||
            cells[0][col].player == player &&
            cells[1][col].player == player &&
            cells[2][col].player == player ||
            row == col &&
            cells[0][0].player == player &&
            cells[1][1].player == player &&
            cells[2][2].player == player ||
            row + col == 2 &&
            cells[0][2].player == player &&
            cells[1][1].player == player &&
            cells[2][0].player == player)
        {
            return player
        } else {
            return Player.NULL
        }
    }

    fun clearCells() {
        currentPlayer = Player.X
        cells = Array(3) {
            Array(3) {
                Cell(Player.NULL)       // 모든 셀 NULL로 초기화
            }
        }
    }

}

Model 역할

 

 

 

 

 

 

 


presenter/PresenterInterface.kt

package com.jwsoft.kotlinproject.presenter

interface PresenterInterface {

    fun onCreate()
    fun onResume()
    fun onPause()
    fun onDestroy()

}

 

 

 

 

 

 

presenter/Presenter.kt

package com.jwsoft.kotlinproject.presenter

import com.jwsoft.kotlinproject.model.Board
import com.jwsoft.kotlinproject.model.Player
import com.jwsoft.kotlinproject.view.MainView

class Presenter(var view: MainView) : PresenterInterface {

    lateinit var model: Board

    override fun onCreate() {
        model = Board()
    }

    override fun onResume() {
    }

    override fun onPause() {
    }

    override fun onDestroy() {
    }

    fun onButtonSelected(row: Int, col: Int) {
        var player = model.mark(row, col)

        if (player != Player.NULL) {
            view.markOnButton(row, col, player.toString())

            if (model.getWinner(row, col, player) != Player.NULL) {
                view.setWinnerLabel(player.toString())
                view.showWinnerViewGroup()

            } else {
                model.flipPlayer()
            }
        }
    }

    fun reset() {
        view.clearButton()      // Presenter -> View (버튼 클리어)
        model.clearCells()      // Presenter -> Model (셀 초기화)
    }

}

Presenter 역할

 

 

 

 

 

 

 


view/MainView.kt

package com.jwsoft.kotlinproject.view

interface MainView {

    fun markOnButton(row: Int, col: Int, player: String)
    fun clearButton()
    fun showWinnerViewGroup()
    fun setWinnerLabel(player: String)

}

 

 

 

 

 

 

view/MainActivity.kt

package com.jwsoft.kotlinproject.view

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import com.jwsoft.kotlinproject.R
import com.jwsoft.kotlinproject.presenter.Presenter
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), MainView {

    var presenter: Presenter = Presenter(this)


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnReset.setOnClickListener {
            presenter.reset()
        }

        presenter.onCreate()        // 생명주기에 대처
    }

    override fun onResume() {
        super.onResume()
        presenter.onResume()        // 생명주기에 대처
    }

    override fun onPause() {
        super.onPause()
        presenter.onPause()         // 생명주기에 대처
    }

    override fun onDestroy() {
        super.onDestroy()
        presenter.onDestroy()       // 생명주기에 대처
    }

    override fun markOnButton(row: Int, col: Int, player: String) {     // Presenter -> View
        btnGrid.findViewWithTag<Button>(row.toString() + col.toString()).text = player
    }

    override fun clearButton() {        // Presenter -> View
        for (i in 0 until btnGrid.childCount) {
            (btnGrid.getChildAt(i) as Button).text = ""
        }
        winnerPlayerViewGroup.visibility = View.GONE
    }

    override fun showWinnerViewGroup() {
        winnerPlayerViewGroup.visibility = View.VISIBLE
    }

    override fun setWinnerLabel(player: String) {
        tvWinnerPlayer.text = player
    }

    fun onClickCell(view: View) {       // View -> Presenter
        var row = view.tag.toString().substring(0, 1).toInt()
        var col = view.tag.toString().substring(1, 2).toInt()

        presenter.onButtonSelected(row, col)        // View -> Presenter
    }

}

View 역할 

 

실행 결과

 

 

 

 

 

참고.

academy.realm.io/kr/posts/eric-maxwell-mvc-mvp-and-mvvm-on-android/

 

안드로이드의 MVC, MVP, MVVM 종합 안내서

안드로이드 앱을 만드는 개발자를 위한 MVC, MVP, MVVM 패턴 사용법과 장단점에 대한 안내서입니다.

academy.realm.io

github.com/ericmaxwell2003/ticTacToe

 

ericmaxwell2003/ticTacToe

A simple tic tac toe app, to illustrate the use of MVC, MVP, and MVVM architectures to organize the application. - ericmaxwell2003/ticTacToe

github.com

 

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함