6 Design Patterns Every Android Developer Must Know

2023. 5. 3. 16:36개발/Android

Singleton

  • 오직 하나의 인스턴스만 허용함
  • 응용프로그램의 모든 부분에서 액세스가 가능
  • singleton클래스는 외부 코드에 의존하지 않고, 자체 인스턴스를 생성해야함
  • singleton 클래스는 인스턴스가 스레드로부터 안전한지 확인해야함
  • 키 속성을 유지하면서, 필요한 경우 쉽게 확장하거나 하위 클래스로 분류할 수 있느 방식으로 설계되어야함
lass Singleton private constructor() {
    companion object {
        private var instance: Singleton? = null
        fun getInstance(): Singleton {
            if (instance == null) {
                instance = Singleton()
            }
            return instance!!
        }
    }
}
  • DB나 네트워크 연결같은….인스턴스가 하나만 있어야하는 시나리오에 유용하다~

 

Factory

  • 인터페이스를 상속받지만, 하위클래스에서 생성될 객체의 유형을 변경할 수 있는것
interface Shape {
    fun draw()
}

class Circle: Shape {
    override fun draw() {
        println("Drawing a Circle")
    }
}

class Square: Shape {
    override fun draw() {
        println("Drawing a Square")
    }
}

class ShapeFactory {
    fun getShape(shapeType: String): Shape? {
        return when (shapeType.toLowerCase()) {
            "circle" -> Circle()
            "square" -> Square()
            else -> null
        }
    }
}
  • 클래스가 생성해야하는 객체 유형을 예상할 수 없거나, 클래스가 객체 생성 책임을 하위 클래스에 위임하려는 경우에 사용

 

Builder

  • 복잡한 객체를 단계별로 구성할 수 있는 생성 디자인 패턴~
  • 고유 메서드로만 인스턴스화가 가능.. 기본 혹은 전용 생성자가 존재해야한다
  • 빌더클래스에는 구성중인 최종 개체를 반환하는 메서드가 있어야한다~
  • 생성되는 최종 개체에는 클라이언트가 개체 생성을 시작할 수 있도록, 빌더 클래스의 새 인스턴스를 반환하는 정적 메서드 또는 팩토리 메서드를 제공해야함
  • 빌더클래스는 변경이 안 되어야한다. 속성이 변경되지않아야한다..
class User {
    var firstName: String? = null
    var lastName: String? = null
    var age: Int? = null
    var phone: String? = null
    var address: String? = null

    override fun toString(): String {
        return "User(firstName=$firstName, lastName=$lastName, age=$age, phone=$phone, address=$address)"
    }
}

class UserBuilder {
    private var firstName: String? = null
    private var lastName: String? = null
    private var age: Int? = null
    private var phone: String? = null
    private var address: String? = null

    fun setFirstName(firstName: String) = apply { this.firstName = firstName }
    fun setLastName(lastName: String) = apply { this.lastName = lastName }
    fun setAge(age: Int) = apply { this.age = age }
    fun setPhone(phone: String) = apply { this.phone = phone }
    fun setAddress(address: String) = apply { this.address = address }

    fun build() = User().apply {
        firstName = this@UserBuilder.firstName
        lastName = this@UserBuilder.lastName
        age = this@UserBuilder.age
        phone = this@UserBuilder.phone
        address = this@UserBuilder.address
    }

  • 복잡한 객체를 생성해야할 때

 

Facade

  • 시스템 작업을 위해 통합된 상위 수준의 api를 제공하는 패턴
interface  UsersApi { 
  @GET( "userss" ) 
  fun  listUsers () : Call<List<User>> 
}

 

DI

  • 이제 이거 그만 말할때 된 것 같음
interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) {
        println(message)
    }
}

class UserService(private val logger: Logger) {
    fun createUser(username: String) {
        // Code to create a user
        logger.log("User $username created")
    }
}

fun main() {
    val userService = UserService(ConsoleLogger())
    userService.createUser("demo")
    // Output: User demo created
}

클래스내에서 인스턴스 생성하지않고, 생성자를 통해 받아서 사용함. 내부 코드에 영향을 주지않고, 변경이 용이

 

Adapter

  • 원래 객체의 인터페이스를 새 인터페이스에 적응시키는 방법. 호환되지 않는 인터페이스를 함께 작동할 수 있게함~
interface MediaPlayer {
    fun play(fileType: String, fileName: String)
}

class MP3Player : MediaPlayer {
    override fun play(fileType: String, fileName: String) {
        if (fileType == "mp3") {
            println("Playing MP3 file: $fileName")
        } else {
            println("Invalid file format")
        }
    }
}

class MediaAdapter(private val mediaType: String) : MediaPlayer {
    private val vlcPlayer = VLCPlayer()
    private val mp4Player = MP4Player()

    override fun play(fileType: String, fileName: String) {
        when (mediaType) {
            "vlc" -> vlcPlayer.playVLC(fileName)
            "mp4" -> mp4Player.playMP4(fileName)
            else -> println("Invalid media format")
        }
    }
}

class VLCPlayer {
    fun playVLC(fileName: String) {
        println("Playing VLC file: $fileName")
    }
}

class MP4Player {
    fun playMP4(fileName: String) {
        println("Playing MP4 file: $fileName")
    }
}

class AudioPlayer : MediaPlayer {
    private val mediaAdapter = MediaAdapter("")

    override fun play(fileType: String, fileName: String) {
        when (fileType) {
            "mp3" -> MP3Player().play(fileType, fileName)
            "vlc", "mp4" -> mediaAdapter.play(fileType, fileName)
            else -> println("Invalid media format")
        }
    }
}

fun main() {
    val audioPlayer = AudioPlayer()

    audioPlayer.play("mp3", "music.mp3")
    // Output: Playing MP3 file: music.mp3

    audioPlayer.play("vlc", "video.vlc")
    // Output: Playing VLC file: video.vlc

    audioPlayer.play("mp4", "movie.mp4")
    // Output: Playing MP4 file: movie.mp4

    audioPlayer.play("avi", "file.avi")
    // Output: Invalid media format
}

 

Common Architectures

  1. MVC : 컨트롤러가 모델과 뷰를 업데이트하는 구조. 컨트롤러가 업데이트한 모델은 뷰가 그걸 참조해서 보여줌
  2. MVP : 위와 비슷하지만.. 모델에서 뷰를 분리했음. 프레젠터가 갱신까지 해줌~
  3. MVVM : 알지? .. 위와 비슷하지만 뷰에 대한 데이터 공급과, 모델에서 뷰 추상화를 담당하는 뷰모델이 생겨남.
  4. 클린 아키텍쳐~ : 어플리케이션을 별개의 레이어로 구분( 데이터 / 도메인 / 프리젠테이션 )
  5. 컴포넌트 기반 아키텍쳐 : MSA
    1. 이벤트 기반 아키텍쳐 : 각 구성요소는 특정 이벤트를 수신하고, 일부 작업을 수행하며, 새 이벤트를 전송해 이벤트에 반응 ( MVI랑 비슷..한가?)

'개발 > Android' 카테고리의 다른 글

Memory leaks  (0) 2023.07.25
Data Store  (0) 2023.07.25
알아두면 좋은 Kotlin extension  (0) 2023.05.03
DiffUtil로 RecyclerView효율화하기  (0) 2023.05.03
onBackPressed() deprecated issuede  (0) 2023.03.10