12/23 5장 객체지향 5대원칙(SOLID)

AI·2025년 12월 23일

사용자 인터페이스 - 화면 설계
tab키 사용 많이 함

객체지향 5대원칙(SOLID)

단일 책임 원칙

public class User {
    int id;
    String name;
    String email;

    void sendWelcomeEmail(User user){
        // user의 email 이용해서 메일 전송
    }
    void saveUser(User user){
        // user 객체 db에 저장
    }
}

를 다음과 같이 분해해서 사용

개방 폐쇄 원칙

public class FeeCalculator {
    public double calculateFee(Object payment, double amount){
        if(payment instanceof NaverPay){
            return amount*0.03;
        } else if(payment instanceof KakaoPay) {
            return amount*0.05;
        }
        // 새로운 결제수단 코드 추가
        return 0;
    }
}

class NaverPay{}
class KakaoPay{}

=>
기본 함수가 안바뀌도록 설정

public class FeeCalculator {
    // 새로운 결제 수단이 생겨도 FeeCalculator 변화 x
    public double calculateFee(PaymentPolicy policy, double amount){
        return policy.getFee(amount);
    }
}

interface PaymentPolicy{
    double getFee(double amount);
}
class NaverPay implements PaymentPolicy{
    @Override
    public double getFee(double amount) {
        return amount*0.03;
    }
}
class KakaoPay implements PaymentPolicy{

    @Override
    public double getFee(double amount) {
        return amount*0.05;
    }

리스코프 교체 원칙

public class Rectangle {
    int width;
    int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }
    public int getArea(){
        return this.width * this.height;
    }
}

// 정사각형 = 가로, 세로 동일하게
// Rectangle 타입 선언 => squre 객체 연결 + setWidth() => 세로 함께 바뀜
// 면적을 구할때, 예상과 다른 결과 발생
class Square extends Rectangle{
    @Override
    public void setWidth(int width){
        this.width = width;
        this.setHeight(width);
    }
}

=>
함수명 같아서 생기는 문제 해결

interface Shape{
    int getArea();
}
public class Rectangle implements Shape{
    int width;
    int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }
    public int getArea(){
        return this.width * this.height;
    }
}

// 정사각형
class Square implements Shape{
    int side;

    public void setSide(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return this.side*this.side;
    }
}

인터페이스 분리 원칙

interface Printer{
    void print();
    void fax();
    void scan();
}
// print 기능만 가짐, 나머지 기능 지원 불가하나 구현은 해야한다
public class BasicPrinter implements Printer{
    @Override
    public void print() {
        System.out.println("print");
    }

    @Override
    public void fax() {}

    @Override
    public void scan() {}
}

=>
기능별로 분리

interface Printer{
    void print();
}
interface Fax{
    void fax();
}interface Scanner{
    void scan();
}

// print 기능만 가짐, 나머지 기능 지원 불가하나 구현은 해야한다
public class BasicPrinter implements Printer{
    @Override
    public void print() {
        System.out.println("print");
    }
}

class FaxMachine implements Fax{

    @Override
    public void fax() {
        System.out.println("fax");
    }
}

class SmartPrinter implements Printer, Fax, Scanner{

    @Override
    public void print() {
        System.out.println("print");
    }

    @Override
    public void fax() {
        System.out.println("fax");
    }

    @Override
    public void scan() {
        System.out.println("scan");
    }
}

의존 관계 역전 원칙

// 새로운 공지가 생기면, 필드 추가
public class NotifyService {
    SmsService smsService = new SmsService();

    void notify(String message){
        smsService.send(message);
    }
}

class SmsService{
    void send(String message){
        System.out.println("SMS : "+ message);
    }
}

=>
객체를 직접받는 것이 아니라 인터페이스를 받는 거
이를 통해 새롭게 정의해줄 필요가 없음

interface MessageSender{
    void send(String message);
}
public class NotifyService {
    MessageSender sender;

    public void setSender(MessageSender sender) {
        this.sender = sender;
    }

    void notify(String message){
        sender.send(message);
    }
}

class SmsService implements MessageSender{
    @Override
    public void send(String message){
        System.out.println("SMS : "+ message);
    }
}

class KakaoService implements MessageSender{
    @Override
    public void send(String message){
        System.out.println("Kakao : "+ message);
    }
}

면접에서도 많이 나오기에 필수적으로 알아두기

패턴 검사나 class 설계를 하면서 위배되는 거는 고침

코드성 컬럼

도서 분류 1건 group_code 에 등록, 도서 분류 세부 항목 '장편 소설', '단편 소설', '에세이', .... 각각 code 에 등록
회원 분류 1건 group_code 에 등록, 회원 분류 세부 항목 '일반 회원', '외국인 회원', .... 각각 code 에 등록
1. 입력 오류 방지
코드화 되어 있지 않으면 도서를 등록할 때 도서 분류 항목이 타이핑하도록 되어 있다. '장편 소설' 문자열로 입력, '장편 쇼설' 같은 잘못된 입력 가능
코드화 되어 있으면 도서를 등록할 때 콤보박스 형태로 선택하도록 select 장편 소설
2. 신규 분류 등록
'판타지 소설' 를 도서 분류로 추가할 경우
코드화 되어 있지 않으면 도서의 분류를 보여주는 곳(모두 하드코딩)을 모두 찾아서 '판타지 소설' 을 추가
코드화 되어 있으면 항상 도서의 분류를 백엔드로부터 목록 데이터를 받아서 처리
백엔드에게 항상 도서 분류 목록 요청 - 반복문으로 select or radio 든 만들도록 구성
백엔드의 코드를 추가만 하면 자동으로 문제 해결
3. 분류 수정
'장편 소설' -> '장편소설' 빈 칸없이 수정
코드화 되어 있지 않으면 도서의 분류를 보여주는 곳(모두 하드코딩)을 모두 찾아서 '장편소설' 로 변경
코드화 되어 있으면 백엔드의 코드를 수정
<= 코드 -> 자바 ENUM 사용 ??
ENUM 은 불변항목 사용 ex. 계절
ENUM 을 코드 대체 <= 코드 가변적
코드가 추가, 변경 <= DB 만 변경되면 모두 해결, ENUM 은 자바코드이므로 ENUM 이 추가되면 서비스 restart 필요
<= 010, 020 ... 가독성이 낮다. '010' -> 'National', 'Foreginer' 가독성 높은 코드를 사용 경향
<= 공통코드 테이블 구성과 컬럼의 구성은 엄청나게 다양하게 많다.
del_yn :
약어 : '국내회원' -> '국내'
order_by : 정렬




0개의 댓글