Study 54

테스트 대역

테스트 대상이 되는 오브젝트 기능만을 충실하게 수행하면서 빠르게 자주 테스트를 실행할 수 있도록 사용하는 오브젝트를 통틀어서 테스트 대역test double이라고 한다. 이를테면 회원가입 비즈니스 로직에 회원가입을 축하한다는 메일을 보내는 로직이 포함되어 있다고 가정하자. UserService class UserService( private val mailSender: MailSender ) { fun signUp(user: User) { //가입 로직 //... //회원가입 메일 전송 val mailMessage = SimpleMailMessage() mailMessage.setTo(user.email) mailMessage.setFrom("useradmin@ksug.org") mailMessage.s..

Study 2021.12.21

데이터 중심 설계의 문제점

캡슐화를 위반한 설계를 구성하는 요소들이 높은 응집도와 낮은 결합도를 가질 확률은 극히 낮다. 따라서 캡슐화를 위반한 설계는 변경에 취약할 수밖에 없다. 데이터 중심 설계가 변경에 취약한 이유는 두 가지다. 너무 이른 시기에 데이터에 관해 결정하도록 강요한다. 협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정한다. 데이터 중심 설계는 객체의 행동보다 상태에 초점을 맞춘다 데이터 중심 설계를 시작할 때 던지는 첫 번째 질문은 "이 객체가 포함해야 하는 데이터가 무엇인가?"이다. 데이터는 구현의 일부다. 데이터 주도 설계에서는 설계를 시작하는 처음부터 데이터를 결정하도록 강요하기 때문에 너무 이른 시기에 내부 구현에 초점을 맞추게 된다. 데이터 중심 설계에 익숙한 개발자는 일반적으로 데이..

Study/오브젝트 2021.11.07

왜 캡슐화를 지켜야 하는가?

'상태'와 '행동'을 하나의 객체로 모으는 이유는 객체 내부 구현을 외부로부터 감추기 위해서다. 여기서 구현이란, 나중에 변경될 가능성이 높은 어떤 것을 가리킨다. 객체지향이 강력한 이유는 한 곳에서 일어난 변경이 전체 시스템에 영향을 끼치지 않도록 파급효과를 적절하게 조절할 수 있는 장치를 제공하기 때문이다. 객체를 사용하면 변경 가능성이 높은 부분은 내부에 숨기고, 외부에는 상대적으로 안정적인 부분만 공개함으로써 변경의 여파를 통제할 수 있다. 변경될 가능성이 높은 부분을 구현이라고 부르고, 상대적으로 안정적인 부분을 인터페이스라고 부른다. 객체 설계는 변경 정도에 따라 구현과 인터페이스를 분리하고 외부에서는 인터페이스만 의존하도록 해야 한다. 이것이 캡슐화다. 캡슐화의 정도가 응집도와 결합도에 영향..

Study/오브젝트 2021.11.07

@ParameterlizedTest

@ParameterlizedTest 는 인자를 받아서 여러 번 테스트를 실행한다. 테스트를 진행하다 보면 거의 같고 약간만 다른 테스트 케이스를 만날 수 있다. 이를 테면 어떤 주차장에 방문 예약하는 테스트를 만든다고 가정하면 아래와 같은 예외 케이스들이 나온다. 핸드폰 번호가 없으면 BadRequestException 이 발생한다. 날짜가 잘못되면 BadRequestException 이 발생한다. 차량번호가 잘못되면 BadRequestException 비활성화된 주차장이라면 ForbbidenException 이런 예외들이 발생할 수 있다. 이걸 하나씩 테스트하면 테스트 코드가 거의 중복된다. @Test fun `방문차량 예약 시 주차장이 비활성화 상태면 ForbbidenException이 발생한다`()..

Study 2021.09.08

Thread 실행하기 - 폴링과 콜백

스레드를 직접 만들어서 실행할 때 보통 Tread 클래스를 서브클래싱하거나 Runnable 인터페이스를 구현한다. 자바 네트워크 프로그래밍 3장 '스레드'를 보면 일반적으로 Thread 를 서브클래싱하는 것보다 Runnable 인터페이스를 구현하는 것을 더 선호해야 할 이유는 없고, 반대의 경우도 마찬가지라고 한다. 하지만 Thread 가 하는 일이 정말로 Thread 가 아니기 때문에 객체지향의 관점에서 보자면 Runnable 을 구현하는 게 맞다고 한다. Thread 가 하는 일이 실제로 Thread 가 아니라는 말은 무슨 말인지 잘 이해 못했다. 어쨌든 Thread 를 확장하는 것보다 Runnable 을 구현해야 하는 게 맞는 것 같다. Java는 다중 상속을 지원하지 않고 Thread 가 아닌 다..

#.1 동작 파라미터화

1. 동작 파라미터화 동작 파라미터화(behavior parameterization)란 아직은 어떻게 실행할지 결정하지 않은 코드 블록을 의미한다. 이 코드 블록은 나중에 프로그램에서 호출한다. 즉, 코드 블록의 실행을 나중으로 미룬다. 예를 들어 나중에 실행될 메서드 인수로 코드 블록을 전달할 수 있다. 결과적으로 코드 블록에 따라 메서드의 동작이 파라미터화된다. 예를 들어 컬렉션을 처리할 때 다음과 같은 메서드를 구현한다고 가정하자. 리스트의 모든 요소에 대해 '어떤 동작'을 수행할 수 있음 리스트 관련 작업을 끝낸 다음, '어떤 다른 동작'을 수행할 수 있음 에러가 발생하면 '정해진 어떤 다른 동작'을 수행할 수 있음 동작 파라미터화로 이처럼 다양한 기능을 수행할 수 있다. 1.1 변화하는 요구사항..

DIP: 의존성 역전 원칙

의존성 역전 원칙에서 말하는 '유연성이 극대화된 시스템'이란 소스 코드 의존성이 추상abstraction에 의존하며 구체concretion에는 의존하지 않는 시스템이다. 자바와 같은 정적 타입 언어에서 이 말은 use, import, include 구문은 오직 인터페이스나 추상 클래스 같은 추상적인 선언만을 참조해야 한다는 뜻이다. 구체적인 대상에 절대로 의존하지 말아야 한다. Ruby나 Python 같은 동적 타입 언어에도 이 규칙이 동일하게 적용된다. 소스 코드 의존 관계에서 구체 모듈은 참조하면 안 된다. 하지만 이들 언어의 경우 구체 모듈이 무엇인지 정의하기 다소 어렵다. 호출할 함수가 구현된 모듈이라면 참조하지 않기가 특히 어렵다. 규칙으로서 DIP는 비현실적이다. 소프트웨어 시스템이라면 구체적..

Study/객체지향 2021.07.31

ISP: 인터페이스 분리 원칙

인터페이스 분리 원칙은 사진1에서 보는 다이어그램에서 그 이름이 유래했다. 사진1에 기술된 상황에서, 다수의 사용자가 OPS 클래스의 오퍼레이션을 사용한다. User1은 오직 op1을, User2는 op2만을, User3은 op3만을 사용한다. 그리고 OPS가 정적 타입언어로 작성된 클래스라고 가정하자. 이 경우 User1에서는 op2와 op3를 전혀 사용하지 않음에도 User1의 소스코드는 이 두 메서드에 의존하게 된다. 이런 의존성으로 인해 OPS 클래스에서 op2의 소스 코드가 변경되면 User1도 다시 (사실 User1과 관련된 전혀 변경되지 않았음에도 불구하고) 컴파일한 후에 다시 배포해야 한다. 이런 문제는 그림2에서 보는 것처럼 오퍼레이션을 인터페이스 단위로 분리하여 해결할 수 있다. 이번에..

Study/객체지향 2021.07.31

LSP: 리스코프 치환 원칙

1988년 바바라 리스코프는 하위 타입을 아래와 같이 정의했다. S 타입 객체 o1 각각에 대응하는 T 타입 객체 o2가 있고 T 타입으 이용해서 정의한 모든 프로그램 P에서 o2의 자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면, S는 T의 하위 타입이다. 리스코프 치환 원칙으로 알려진 이 개념을 이해하기 위한 몇 가지 예제가 있다. 상속을 사용하도록 가이드 사진1과 같이 License라는 클래스가 있다고 가정하자. 이 클래스는 calcFee()라는 메서드를 가지며, Billing 애플리케이션에서 이 메서드를 호출한다. License에는 PersonalLicense와 BusinessLicense라는 두 가지 하위 타입이 존재한다. 이들 두 하위타입은 서로 다른 알고리즘을 이용해서 라이선스 비용을..

Study/객체지향 2021.07.13

OCP: 개방 폐쇄 원칙

소프트웨어 개체artifact는 확장에 열려있어야 하고 변경에 닫혀 있어야 한다. 다시 말해 소프트웨어 개체의 행위는 확장 가능해야 하지만, 이때 개체를 변경해서는 안 된다. 소프트웨어 아키텍처를 공부하는 가장 근본적인 이유가 바로 이것 때문이다. 소프트웨어 설계를 공부하기 시작한 지 얼마 안 된 사람들은 OCP를 클래스와 모듈을 설계할 때 도움되는 원칙이라고 안다. 하지만 아키텍처 컴포넌트 수준에서 OCP를 고려할 때 훨씬 중요한 의미를 가진다. 사고 실험 재무제표를 웹페이지로 보여주는 시스템이 있다고 생각하자. 웹 페이지에 표시되는 데이터는 스크롤할 수 있고, 음수는 빨간색으로 출력한다. 이제 이해관계자가 동일한 정보를 보고서 형태로 변환해서 흑백 프린터로 출력해 달라고 요청했다고 하자. 이 보고서에..

Study/객체지향 2021.07.10