반응형
Heli, 헬리
시행착오를 줄이는 방법 - 진태양
Heli, 헬리
  • 분류 전체보기 (82)
    • General (28)
      • Essay (22)
      • Craftsmanship (2)
      • IT Meet & Hack (4)
    • Finance (1)
      • Portfolio (1)
      • Activity (0)
    • Infrastructure (1)
      • Kubernetes (0)
      • AWS (1)
    • Development (45)
      • News (4)
      • Architecture (4)
      • Web (1)
      • Spring Framework (7)
      • JVM (12)
      • MongoDB (0)
      • Git (2)
      • Algorithm (14)
      • Python (1)
    • Computer Science (1)
      • Network (1)
    • Civic Hacking (3)
      • Code for Korea (3)
    • Know-how (2)
      • IT Service (1)
      • Career (1)
    • English (1)
      • Translation (1)

인기 글

  • 서버 개발자, 커뮤니티 빌더의 이야기가 궁금하신분!
    2023.03.28
    서버 개발자, 커뮤니티 빌더의 이야기가 궁금하신분!
  • Why DDD, Clean Architecture and ⋯
    2022.03.10
    Why DDD, Clean Architecture and ⋯
  • [번역] 개발자가 잠자는 동안 돈을버는 5가지 방법 | 사⋯
    2022.04.17
    [번역] 개발자가 잠자는 동안 돈을버는 5가지 방법 | 사⋯
  • M1 칩에서 pyqt5 설치하기 - qmake 패스 설정
    2022.07.30
  • [Java & Kotlin] enum class가 완벽한 ⋯
    2021.12.13
    [Java & Kotlin] enum class가 완벽한 ⋯

블로그 메뉴

  • 홈
  • 관리
  • 방명록
hELLO · Designed By 정상우.
Heli, 헬리

시행착오를 줄이는 방법 - 진태양

[토비의 스프링 3.1] 1장 오브젝트와 의존관계
Development/Spring Framework

[토비의 스프링 3.1] 1장 오브젝트와 의존관계

2022. 3. 5. 15:40
반응형

Reflection, Default Constructor

  • Reflection을 이용해 오브젝트를 생성하기 때문에 Default Constructor가 필요하다고 이야기 함.
  • 코드가 어떤식으로 작성되기 때문에 그런걸까?
val someClass = SomeClass::class.java
val constructors = someClass.getDeclaredConstructor()
constructors.isAccessible = true
val instance = constructors.newInstance()

Class에 대한 Reference 획득 → 해당 Class의 생성자 획득 → 생성자를 사용할 수 있게끔 접근 지정자 public 으로 변경 → 생성자를 이용해 인스턴스 생성

이와 같이 Default Constructor를 통한 Instance 생성 → 생성된 Instance에 Field 세팅(IoC 컨테이너를 통한 DI 등) 순서로 이루어지기 떄문에 Default Constructor가 필수적임.

[Java & Kotlin] enum class가 완벽한 싱글톤이라 불리는 이유

 

[Java & Kotlin] enum class가 완벽한 싱글톤이라 불리는 이유

싱글톤 패턴(Singleton Pattern)이란? 싱글톤은 애플리케이션 상 특정 클래스가 최초 한 번만 메모리를 할당하고 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴을 의미한다. 객체 생성 요청이

dataportal.kr

 

관심사의 분리

  • 애플리케이션을 설계할 때에는 기존의 의존관계가 변경되거나 어떠한 역할을 하는 오브젝트가 사라질 수 있음을 염두에 두고 설계해야만 한다.
  • 하지만 쉽지는 않은 일이다. 그렇지만 조금이나마 쉽게 이를 실천할 수 있는 방법이 있다.

환경, 상황 그리고 요구사항이 변함에 따라서 기능 변경을 위한 코드 레벨의 변화를 최소한으로 줄일 수 있는 방향으로 항상 생각하라.

그럼 어떻게 변경이 일어날 때 작업을 최소화하고, 그 변경이 다른 곳에 문제를 일으키지 않게 할 수 있을까? → 분리와 확장을 고려한 설계

분리와 확장 예시

  1. Database Source의 변경: MySQL → Oracle
  2. Log Format 변경: 20자리 텍스트 포맷 → 16자리 텍스트 포맷
  3. 등등,,

만약 이러한 변경을 적용할 때 여러 곳에 break-change가 발생한다면? → ......

가장 쉽게 이를 해결하는 방법은 공통 로직의 분리 → DatabaseConnectionManager, Logger, ...

Template Method Pattern

  • Super-class 에 기본적인 흐름을 두고 기능 중 일부를 직접 구현하거나 Sub-class 에서 구현
abstract class UserDao {
    fun findById(id: Long): User {
        val connection = getConnection()
        // ....
        return User(/*....*/)
    }

    protected abstract fun getConnection(): Connection
}

class KakaoUserDao : UserDao() {
    override fun getConnection(): Connection {
        // ...
        return Connection(/*....*/)
    }
}

class NaverUserDao : UserDao() {
    override fun getConnection(): Connection {
        // ...
        return Connection(/*....*/)
    }
}

결국 객체지향 설계 원칙을 잘 지키는게 중요하다

  • 단일 책임 원칙(SRP; Single Responsibility Principle)
    : 각 클래스는 고유한 하나의 역할만 지녀야 한다.
  • 개방 폐쇄 원칙(OCP; Open-Closed Principle)
    : 확장엔 열려있고, 수정엔 닫혀있다. 즉 기존의 모듈을 수정하지 않고 확장할 수 있어야한다.
  • 리스코프 치환 원칙(LSP; Liskov Substitution Principle)
    : 상위 타입의 객체를 하위 타입으로 치환해도 정상적으로 동작해야 한다
  • 인터페이스 분리 원칙(ISP; Interface Segregation Principle)
    : Client에서 요구하는 Interfacing만 지원하게끔 인터페이스를 분리해야 한다.
  • 의존관계 역전 원칙(DIP; Dependency Inversion Principle)
    : 저수준 모듈이 고수준 모듈에 의존해야 한다.
    • 고수준 모듈: 알림
    • 저수준 모듈: KakaoTalk 알림, SMS 알림, E-mail 알림, ...

// DipExam
interface Alarm {
    fun beep(): String
}

class KakaoTalkAlarm : Alarm {
    override fun beep(): String {
        return "KakaoTalk"
    }
}

class SmsAlarm : Alarm {
    override fun beep(): String {
        return "Sms"
    }
}

class EmailAlarm : Alarm {
    override fun beep(): String {
        return "Email"
    }
}

class AlarmCenter(private val alarm: Alarm) {
    fun alarm() {
        println(alarm.beep())
    }
}

fun main() {
    val kakaoTalkAlarm = KakaoTalkAlarm()
    val alarmCenter = AlarmCenter(kakaoTalkAlarm)

    alarmCenter.alarm()
}

Strategy Pattern

객체지향 설계 원칙에 따라 개발을 하다보면 가장 흔히 쓰이는 디자인 패턴 중 하나.

전략 패턴은 각 구현체별로 다르게 가져가야 하는 기능을 인터페이스를 통해 통째로 외부에 분리시키고, 이를 구현한 구현 클래스를 필요, 상황에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴이다.

그러다보니 객체지향 설계 원칙 중, 개방 폐쇄 원칙(OCP)의 실현에 가장 잘 들어맞는 패턴이라 볼 수 있다.

Object Factory Class

feat: ObjectFactory 예제 추가 · heli-os/workspace-for-blog@3741479

 

feat: ObjectFactory 예제 추가 · heli-os/workspace-for-blog@3741479

Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files feat: ObjectFactory 예제 추가 Loading branch information Showing 1 changed file with 81 additions and 0 deletions.

github.com

예제 소스코드

// ConnectionMaker
interface ConnectionMaker {
    fun getConnection()
}

class KakaoConnectionMaker : ConnectionMaker {
    override fun getConnection() {
    }
}

// Dao using on Client
interface Dao {
    fun run()
}

class AccountDao(
    private val connectionMaker: ConnectionMaker
) : Dao {

    override fun run() {
        /*..*/ connectionMaker.getConnection()
        println("accountDao run()")
    }
}

class RecommendDao(
    private val connectionMaker: ConnectionMaker
) : Dao {

    override fun run() {
        /*..*/ connectionMaker.getConnection()
        println("userDao run()")
    }
}

// Factory
object DaoFactory {

    fun accountDao() = AccountDao(connectionMaker = connectionMaker())

    fun recommendDao() = RecommendDao(connectionMaker = connectionMaker())

    private fun connectionMaker(): ConnectionMaker {
        return KakaoConnectionMaker()
    }
}

// Client
class DaoClient private constructor(
    private val dao: Dao
) {

    fun runDao() {
        dao.run()
    }

    companion object {
        operator fun invoke(dao: Dao): DaoClient {
            println("invoke DaoClient")
            return DaoClient(dao = dao)
        }
    }
}

fun main() {
    val accountDao = DaoFactory.accountDao()
    val daoClientWithAccountDao = DaoClient(dao = accountDao)
    daoClientWithAccountDao.runDao()

    println("========")

    val recommendDao = DaoFactory.recommendDao()
    val daoClientWithRecommendDao = DaoClient(dao = recommendDao)
    daoClientWithRecommendDao.runDao()
}

 

Spring Application Context의 동작방식

  1. 별도로 정의된 설정정보를 기반으로 Bean Class의 시그니처 관리
    ( @Configuration + @Bean / @Component ... )
  2. 관리되고 있는 Bean Class Instance 생성 / 등록
  3. Client에서 Bean Class 조회 요청 시 생성해둔 Instnace 제공

Object Factory Class ↔ Spring Application Context

  1. Application Context는 사용하는쪽에서 구체적인 팩토리 클래스를 알 필요 없게 만들어준다.
  2. Application Context는 Object Factory Class에서 제공하는 기능 + @를 제공한다.
    : Instance 자동 생성, 후처리, 필터, 인터셉터 등등,..
  3. 결국 프레임워크 레벨에서 IoC를 비롯한 기능을 제공해주고 있기 때문에 개발 편의성이 크게 향상된다 볼 수 있다.

싱글톤 레지스트리(singleton registry)

Application Context 는 Object Factory Class와 비슷한 방식으로 동작하는 IoC 컨테이너 이면서 싱글톤 레지스트리이기도 하다. 스프링은 기본적으로 별다른 설정을 하지 않으면 내부에서 생성하는 빈 오브젝트를 모두 싱글톤으로 만든다. Why?

Thread 관점에서의 Web Server Application

  • 웹 (HTTP)은 클라이언트의 요청에 따라 새로운 연결이 수립되고, 요청의 종류에 따라 비즈니스 로직이 실행되며 관련 Instance가 생성되게 된다.
  • Instance 생성은 결국 메모리 할당이다. 만약 1억건의 요청이 들어왔다고 1억 x n개의 Instance를 생성한다면 어떻게 될까? → 메모리 boom

정리

  • 확장, 지속 가능한 애플리케이션을 개발하기 위해선 객체와 각 객체간의 의존관계에 대해서 끝없이 고민해야한다.
  • 결국 각 객체가 어떤 객체와 어떤 메시지를 주고 받아야하는지 정의하는건 개발자이다.
  • 스프링은 원칙을 따라 개발하는데 있어 번거로운 작업을 줄여 이를 실천하는데 큰 도움을 준다.
  • overall dependency-injection-framework repository

GitHub - heli-os/dependency-injection-framework-in-kotlin

 

GitHub - heli-os/dependency-injection-framework-in-kotlin

Contribute to heli-os/dependency-injection-framework-in-kotlin development by creating an account on GitHub.

github.com

 

반응형
저작자표시 비영리 동일조건

    ☕️ Networking

    기술 직군의 기술적인 교류, 커리어 이야기, 직군 무관 네트워킹 모두 환영합니다!

    위클리 아카데미 오픈 채팅방(비밀번호: 9323)

    kakaotalk: https://open.kakao.com/o/gyvuT5Yd

    'Development/Spring Framework' 카테고리의 다른 글
    • 점프 투 스프링부트 / Jump to SpringBoot 출간(?)
    • [토비의 스프링 3.1] 2장 테스트
    • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)
    • SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (2)
    코틀린 스프링부트, 코프링, 토비, 토비의 스프링, 토프링
    Heli, 헬리
    Heli, 헬리
    Java/Kotlin, Spring 백엔드 관련 기술을 익히고 공유합니다.
    [토비의 스프링 3.1] 2장 테스트
    다음 글
    [토비의 스프링 3.1] 2장 테스트
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)
    이전 글
    SpringBoot에서 STOMP로 채팅 애플리케이션 만들기 (3)

    티스토리툴바