다형성과 설계

황상익·2024년 4월 2일

Inflearn JAVA

목록 보기
23/61

Spring 기본편에서 객체지향 프로그래밍에 대해 설명한 적이 있다. 따라서 여기서는 간략하게 적겠다.

객체지향 프로그래밍은 객체들의 모임으로 파악하고자 하고, 각각의 객체는 메시지를 주고 받고, 데이터를 처리
프로그램을 유연하고 변경에 용이

역할과 구현을 분리
역할과 구현으로 구분, 단순, 유연해지면서 변경에도 용이

장점
클라이언트는 대상의 역할(인터페이스)만 알면 된
내부구조는 몰라도 됨
내부구조 변경되도 영향 X
대상 자체를 변경해도 영향 X

자바언어의 다형성 활용
역할 = 인터페이스, 구현 = 인터페이슬르 구현한 클래스, 구현 객체
객체를 설계 할때 역할과 구현 분리

객체의 협력이라는 관계 부터 생각!
혼자 있는 객체는 X
client = 요청, Server = 응답

다형성 본질
인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경
다형성의 본질을 이해하려면 협력이라는 객체사이의 관계에서 시작
클라이언트를 변경X, Server의 구현 기능을 유연하게 변경

역할과 구현을 분리
한계
인터페이스 변경시 -> 변경 커진다.

다형성 역할과 구현 예제

public class K3Car {
    public void startEngine(){
        System.out.println("K3Car.startEngine");
    }

    public void offEngine(){
        System.out.println("K3Car.stopEngine");
    }

    public void pressAccelerator(){
        System.out.println("K3Car.pressAccelerator");
    }
}

public class Model3Car {
    public void startEngine(){
        System.out.println("Model3Car.startEngine");
    }

    public void offEngine(){
        System.out.println("Model3Car.stopEngine");
    }

    public void pressAccelerator(){
        System.out.println("Model3Car.pressAccelerator");
    }
}
public class Driver {
    private K3Car k3Car; //처음은 null 값
    private Model3Car model3Car;

    public void setDriver(K3Car k3Car) {
        this.k3Car = k3Car;
    }

    public void setModel3Car(Model3Car model3Car) {
        this.model3Car = model3Car;
    }

    public void drive(){
        System.out.println("자동차를 운전합니다");

        if (k3Car != null){
            k3Car.startEngine();
            k3Car.pressAccelerator();
            k3Car.offEngine();
        } else if (model3Car != null){
            model3Car.startEngine();
            model3Car.pressAccelerator();
            model3Car.offEngine();
        }
    }
}
public class CarMain0 {
    public static void main(String[] args) {
        Driver driver = new Driver();
        K3Car k3Car = new K3Car();
        Model3Car model3Car = new Model3Car();

        driver.setDriver(k3Car); //자동차를 갖게 됨
        driver.drive();

        System.out.println("====================");
        driver.setDriver(null); //참조 제거
        driver.setModel3Car(model3Car);
        driver.drive();
    }
}

다형성 역활과 구현 예제 2

public interface Car {
    void startEngine();
    void pressAccelerator();
    void offEngine();
}
public class CarMain1 {
    public static void main(String[] args) {
        Driver driver = new Driver();

        K3 k3 = new K3();
        driver.setCar(k3);
        driver.driver();

        Car model3Car = new Model3Car();
        driver.setCar(model3Car);
        driver.driver();

        Car newCar = new NewCar();
        driver.setCar(newCar);
        driver.driver();
    }
}
public class Driver {

    private Car car;

    public void setCar(Car car){
        System.out.println("자동차를 설정합니다 " + car);
    }

    public void driver(){
        System.out.println("자동차를 운전합니다");
        car.startEngine();
        car.pressAccelerator();
        car.offEngine();
    }
}
public class K3 implements Car{
    @Override
    public void startEngine() {
        System.out.println("K3.startEngin");
    }

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

    @Override
    public void offEngine() {
        System.out.println("K3Car.stopEngine");

    }
}
public class Model3Car implements Car{
    @Override
    public void startEngine() {
        System.out.println("Model3Car.startEngine");

    }

    @Override
    public void pressAccelerator() {
        System.out.println("Model3Car.pressAccelerator");

    }

    @Override
    public void offEngine() {
        System.out.println("Model3Car.stopEngine");

    }
}
public class NewCar implements Car{
    @Override
    public void startEngine() {
        System.out.println("car.startEngine");
    }

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

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

먼저 Driver 와 K3Car 를 생성한다.
driver.setCar(k3Car) 를 호출해서 Driver 의 Car car 필드가 K3Car 의 인스턴스를 참조하도록 한
다.
driver.drive() 를 호출하면 x001 을 참조한다. car 필드가 Car 타입이므로 Car 타입을 찾아서 실행
하지만 메서드 오버라이딩에 의해 K3Car 의 기능이 호출된다.


Model3Car 를 생성한다.
driver.setCar(model3Car) 를 호출해서 Driver 의 Car car 필드가 Model3Car 의 인스턴스를 참조
하도록 변경한다.
driver.drive() 를 호출하면 x002 을 참조한다. car 필드가 Car 타입이므로 Car 타입을 찾아서 실행
하지만 메서드 오버라이딩에 의해 Model3Car 의 기능이 호출된다

OCP 원칙
확장에는 OPEN! 변경에는 닫혀있다.

확장에 열려있다는 의미
인터에스를 사용해서 새로운 차량을 자유롭게 추가 가능, Driver도 Car 인터페이스를 통해 새롭게 추가된 차량을 자유롭게 호출 가능

코드 수정에는 닫혀 있다는 의미
새로운 차를 추가, 기능 추가되기 때문에 기존 코드의 수정은 불가피

문제풀이
1.

public interface Message {
    void sendMessage(String message);
}
public class FaceBookSender implements Message{
    @Override
    public void sendMessage(String message) {
        System.out.println("페메를 보냅니다 " + message);
    }
}
public class EmailSender implements Message {
    @Override
    public void sendMessage(String message) {
        System.out.println("메일을 발송합니다 " + message);
    }
}
public class SmsSender implements Message{
    @Override
    public void sendMessage(String message) {
        System.out.println("SMS를 발송합니다 " + message);
    }
}
public class SendMain {
    public static void main(String[] args) {
        Message[] message = {new EmailSender(), new FaceBookSender(), new SmsSender()};

        for (Message m : message) {
            m.sendMessage("환영합니다.");
        }
    }
}
public interface Pay {
    boolean pay (int amount);
}
public class DefaultPay implements Pay {
    @Override
    public boolean pay(int amount) {
        System.out.println("잘못된 결제입니다.");
        return false;
    }
}
public class KakoPay implements Pay{
    @Override
    public boolean pay(int amount) {
        if (amount > 0) {
            System.out.println("카카오페이 시스탬과 연결합니다");
            System.out.println(amount + " 원 결제를 시도합니다 ");
        } else {
            System.out.println("amount가 0 입니다.");
        }

        return true;
    }
}
public class NeverPay implements Pay {
    @Override
    public boolean pay(int amount) {

        if (amount > 0) {
            System.out.println("네이버페이 시스탬과 연결합니다");
            System.out.println(amount + " 원 결제를 시도합니다 ");
        } else {
            System.out.println("amount가 0 입니다.");
        }

        return true;
    }
}
public class PayService {
    private NeverPay neverPay;
    private KakoPay kakoPay;

    public void processPay(String option, int amount) {
        System.out.println("결제를 시작합니다 :" + option + " , amount :" + amount);

        Pay pay = PayStore.findPay(option);
        boolean rst = pay.pay(amount);

        if (amount > 0) {
            if (rst) {
                System.out.println("결제 성공");
            } else {
                System.out.println("결제 실패 ");
            }
        } else {
            System.out.println("다시 입력하세요");
        }

    }
}
public abstract class PayStore {

    public static Pay findPay(String option){
        if (option.equals("Kakao")){
            return new KakoPay();
        } else if (option.equals("never")){
            return new NeverPay();
        } else {
            return new DefaultPay();
        }
    }
}
public class PayMain {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        PayService service = new PayService();

        while (true) {
            System.out.print("결제 수단을 입력하세요 : ");
            String option = sc.nextLine();

            System.out.print("금액을 입력하세요 : ");
            int amount = sc.nextInt();
            if (amount == 0) {
                System.out.println("다시 입력하세요 ");
                return;
            }

            if (option.equals("exit")){
                System.out.println("BYE BYE");
                return;
            }

            service.processPay(option, amount);
        }
    }
}
public class Payment {
    public static void main(String[] args) {
        PayService service = new PayService();

        String payOption1 = "Kakao";
        int amount1 = 0;
        service.processPay(payOption1, amount1);

        String payOption2 = "naver";
        int amount2 = 10000;
        service.processPay(payOption2, amount2);

        String payOption3 = "bad";
        int amount3 = 15000;
        service.processPay(payOption1, amount3);
    }
}
profile
개발자를 향해 가는 중입니다~! 항상 겸손

0개의 댓글