Young Jun Park (박영준)
Java back-end developer
Park Young Jun
Github : https://github.com/june0313
Y3S Study group : https://github.com/y3s-study
2018.07.10
자바를 사용해 만든 소프트웨어는
객체 지향 소프트웨어인가?
객체 지향 언어를 이용해 객체 지향 프로그램을 올바르게 설계해 나가는 방법이 존재할까?
Robert C. Martin
2000년대 초반 객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙을 제시
SRP (Single Responsibility Principle) : 단일 책임 원칙
OCP (Open Close Principle) : 개방 폐쇄 원칙
LSP (Liskov Substitution Principle) : 리스코프 치환 원칙
ISP (Interface Segregation Principle) : 인터페이스 분리 원칙
DIP (Dependency Inversion Principle) : 의존 역전 원칙
"어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다." - Robert C. Martin
public class Student {
public Course getCourse() {/*...*/}
public void addCourse(Course course) {/*...*/}
public void save() {/*...*/}
public Student load() {/*...*/}
public void printOnReportCard() {/*...*/}
public void printOnAttendanceBook() {/*...*/}
}
너무 많은 책임(역할)을 수행하는 Student 클래스
public class Student {
public Course getCourse() {/*...*/}
public void addCourse(Course course) {/*...*/}
public void save() {/*...*/}
public Student load() {/*...*/}
public void printOnReportCard() {/*...*/}
public void printOnAttendanceBook() {/*...*/}
}
class Student {
public Course getCourse() {/*...*/}
public void addCourse(Course course) {/*...*/}
}
class StudentRepository {
public void save(Student student) {/*...*/}
public Student load() {/*...*/}
}
class ReportCard {
public void print(Student student) {/*...*/}
}
class AttendanceBook {
public void print(Student student) {/*...*/}
}
학생
학생 Repository
성적표
출석부
public class 강아지 {
final static Boolean 수컷 = true;
final static Boolean 암컷 = false;
Boolean 성별;
void 소변보다() {
if (this.성별 == 수컷) {
// 한 쪽 다리를 들고 소변을 본다.
} else {
// 뒷다리 두 개를 굽혀 앉은 자세로 소변을 본다.
}
}
}
메서드가 SRP를 지키지 않을 경우 나타나는 대표적인 냄새가 바로
분기 처리를 위한 if문!
abstract class 강아지 {
abstract void 소변보다();
}
class 수컷강아지 extends 강아지 {
void 소변보다() {
// 한 쪽 다리를 들고 소변을 본다.
}
}
class 암컷강아지 extends 강아지 {
void 소변보다() {
// 뒷다리 두 개로 앉은 자세로 소변을 본다.
}
}
@Component
public class UserService {
public void execute() {
System.out.println("UserService execute()");
}
}
UserService 클래스의 execute() 메소드가 실행될때 앞, 뒤에서 로깅을 남기고 싶다면?
@Component
public class UserService {
private static Logger log = LoggerFactory.getLogger(UserService.class);
public void execute() {
log.info("### before logging ###");
System.out.println("UserService execute()");
log.info("### after logging ###");
}
}
@Aspect
@Component
public class LoggingAspect {
private static Logger log = LoggerFactory.getLogger(LoggingAspect.class);
@Before("execution(* net.y3s.designpatternexample.chapter03.srp.UserService.*(..))")
public void loggingBefore(JoinPoint joinPoint) {
log.info("### before logging ###");
}
@After("execution(* net.y3s.designpatternexample.chapter03.srp.UserService.*(..))")
public void loggingAfter(JoinPoint joinPoint) {
log.info("### after logging ###");
}
}
@Component
public class UserService {
public void execute() {
System.out.println("UserService execute()");
}
}
UserService에는 핵심 비즈니스 로직만 남길 수 있다 (SRP 만족)
애플리케이션의 경계를 정하고 추상화를 통해 클래스의 속성과 메서드를 설계할 때
반드시 단일 책임 원칙을 고려하는 습관을 들이자!
"소프트웨어 엔티티는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다." - Robert C. Martin
도서관 대여 명부 출력 기능이 추가되어야 한다면?
DB가 Oracle에서 MySQL로 바뀌더라도 애플리케이션을 수정할 필요가 없다.
OCP를 무시하고 프로그램을 작성하면 객체지향의 큰 장점인 유연성, 재사용성, 유지보수성 등을 얻을 수 없다.
"서브타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다."
- Robert C. Martin
상위 타입 : 표유류는 알을 낳지 않는다.
public class Bag {
private int price;
public void setPrice(int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
}
class DiscountedBag extends Bag {
private double discountedRate = 0;
public DiscountedBag(double discountedRate) {
super();
this.discountedRate = discountedRate;
}
@Override
public void setPrice(int price) {
super.setPrice(price - (int)(discountedRate * price));
}
}
Bag bag = new Bag();
bag.setPrice(50000);
System.out.println(bag.getPrice());
Bag bag = new DiscountedBag(5);
bag.setPrice(50000);
System.out.println(bag.getPrice());
LSP 위반
LSP 만족
할아버지 = new 딸();
동물 = new 박쥐();
LSP는 객체 지향의 상속이라는 특성을 올바르게 활용하면 자연스럽게 만족하게 된다.
"클라이언트는 자신의 사용하지 않는 메서드에 의존 관계를 맺으면 안된다."
- Robert C. Martin
"고차원 모듈은 저차원 모듈에 의존하면 안된다.
이 두 모듈은 모두 다른 추상화된 것에 의존해야 한다."
"추상화된 것은 구체적인 것에 의존하면 안된다. 구체적인 것이 추상화된 것에 의존해야 한다."
"자주 변경되는 구체(Concrete) 클래스에 의존하지 마라."
- Robert C. Martin
class Kid {
private Toy toy;
void setToy(Toy toy) {
this.toy = toy;
}
void play() {
System.out.println(toy.toString());
}
}
abstract class Toy {
public abstract String toString();
}
class Lego extends Toy {
@Override
public String toString() {
return "Lego";
}
}
class Robot extends Toy {
@Override
public String toString() {
return "Robot";
}
}
public class Client {
public static void main(String[] args) {
Kid kid = new Kid();
Toy toy = new Lego();
kid.setToy(toy);
kid.play();
toy = new Robot();
kid.setToy(toy);
kid.play();
}
}
By Young Jun Park (박영준)
SOLID 원칙을 설명합니다.