로버트 C.마틴이 2000년대 초반 객체 지향 프로그래밍 및 설계의 5가지 기본원칙으로 제시한 것
public class UserService {
public void addUser(){ // 사용자 추가 기능
... // 암호화 하는 로직
... // 사용자 코드 자동 생성 로직
}
}
/*
암호화 방식 개선, 사용자 코드 자동 생성 규칙 변경이라는 요구사항이 생길 시 하나의 모듈에 수정해야 하는 이유도 여러개가 됨
*/
public class UserService {
private PasswordEncoder passwordEncoder;
private UserCodeCreater userCodeCreater;
public void addUser(){ // 사용자 추가 기능
// 메서드 호출
passwordEncoder.encode();
userCodeCreater.createUserCode();
}
}
public class PasswordEncoder {
public void encode(){
... // 암호화 하는 로직
}
}
public class UserCodeCreater {
public void createUserCode(){
... // 사용자 코드 자동 생성 로직
}
}
/*
각각 책임사항에 대해 요구사항이 생길 시 수정해야될 클래스가 명확해짐
*/
public class StandardPasswordEncoder {
public void encode(){
... // 암호화 하는 로직
}
}
public class UserService {
// private PasswordEncoder passwordEncoder; // 기존
private StandardPasswordEncoder passwordEncoder; // 변경
...
}
/*
새로운 암호화 정책을 적용하려고 했더니 수정했던 부분과 무관한 UserService 코드를 수정해야하는 상황이 발생함.
-> 변경에 닫히는 원칙에 위반됨
*/
public interface PasswordEncoder{
void encode();
}
public class StandardPasswordEncoder implements PasswordEncoder {
@Override
public void encode(){
... // 암호화 하는 로직
}
}
public class UserService {
private PasswordEncoder passwordEncoder; // 기존대로 사용
// private StandardPasswordEncoder passwordEncoder; // 변경안해도 됨
...
}
/*
StandardPasswordEncoder가 PasswordEncoder를 의존하도록 하여 개방 폐쇄의 원칙에 충족되도록 할 수 있음.
*/
public class Rectangle {
private int width;
private int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
public class Square extends Rectangle {
@Override
public void setWidth(int width, int height) { // 형식이 맞지 않으면 에러 발생
...
}
...
}
public class Rectangle {
...
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
...
}
public class Square extends Rectangle {
@Override
public void setWidth(int width) { // 의도가 달라지면 다른 결과가 나올 수 있음
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) { // 의도가 달라지면 다른 결과가 나올 수 있음
super.setWidth(height);
super.setHeight(height);
}
/*
자식 클래스가 부모 클래스의 메서드에 담긴 의도를 위반 하면 의도하지 않는 결과가 나올 수 있음
*/
}
public interface PasswordEncoder{
void encode();
}
public class StandardPasswordEncoder implements PasswordEncoder {
@Override
public void encode(){
... // 암호화 하는 로직
}
public boolean validate() {
... // 비밀번호 규칙이 맞는지 체크
}
}
/*
validate() 메서드를 사용하기 위해서는 구체 클래스인 StandardPasswordEncoder를 주입받아야 하는데 그러면 불필요한 encode() 메서드까지 접근 가능해지며 인터페이스 분리 원칙을 위배하게 됨
*/
public interface PasswordChecker{
void validate();
}
public class StandardPasswordEncoder implements PasswordEncoder, PasswordChecker {
@Override
public void encode(){
... // 암호화 하는 로직
}
@Override
public boolean validate() {
... // 비밀번호 규칙이 맞는지 체크
}
}
/*
위의 상황을 해결하기 위해서는 비밀번호를 검사하는 별도의 인터페이스를 만들고 인터페이스로 주입받도록 하는 것이 적합
*/