ABOUT ME

Today
Yesterday
Total
  • [JAVA] 자바 basic 9 - 12 강 요약
    자바 2024. 11. 20. 14:39

    9. 상속

    상속 관계

    상속은 객체 지향 프로그래밍의 핵심 요소 중 하나로,

    기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해 준다

    이름 그대로 기존 클래스의 속성과 기능을 그대로 물려받는 것이다

    상속을 사용하려면 extends 키워드를 사용하면 된다

    extends 키워드 대상은 하나만 선택할 수 있다 (단일상속)

     

    - 부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스

    - 자식 클래스 (서브 클래스) : 부모 클래스로부터 필드와 메서드를 상속받는 클래스

     

    public class Car {
        // 부모 클래스 (슈퍼 클래스)
        public void move() {
            System.out.println("차를 이동합니다.");
        }
    }
    public class ElectricCar extends Car {
        // 자식 클래스 (서브 클래스)
        public void charge() {
            System.out.println("충전합니다.");
        }
    }
    
    public class GasCar extends Car {
        // 자식 클래스 (서브 클래스)
        public void fillUp() {
             System.out.println("기름을 주유합니다.");
        }
    }
    public class CarMain {
        public static void main(String[] args) {
            
            ElectricCar electricCar = new ElectricCar();
            electricCar.move();
            electricCar.charge();
            
            GasCar gasCar = new GasCar();
            gasCar.move();
            gasCar.fillUp();
            
        }
    }

    상속은 부모의 기능을 자식이 물려받는 것이다

    따라서 자식이 부모의 기능을 물려 받아서 사용할 수 있지만 부모 클래스는 자식 클래스에 접근할 수 없다

     

     

    상속과 메모리 구조

    상속 관계의 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성된다

    상속 관계의 객체를 호출할 때, 대상 타입을 정해야 한다 이때 호출자의 타입을 통해 대상 타입을 찾는다

    현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행한다 (기능을 찾지 못하면 컴파일 오류)

     

     

    상속과 메서드 오버라이딩

    부모 타입의 기능을 자식에서는 다르게 재정의 하고 싶을 수 있다

    이렇게 부모에게서 상속 받은 기능을 자식이 재정의 하는 것을 메서드 오버라이딩(Overriding) 이라 한다

    public class ElectricCar extends Car {
        
        @Override
        public void move() {
            System.out.println("전기차를 빠르게 이동합니다.");
        }
        
        public void charge() {
            System.out.println("충전합니다.");
        }
    }

     

     

    오버로딩(Overloading)과 오버라이딩(Overriding)
    • 메서드 오버로딩 : 메서드 이름이 같고 매개변수(파라미터)가 다른 메서드를 여러 개 정의하는 것
    • 메서드 오버라이딩 : 메서드 오버라이딩은 하위 클래스에서 상위 클래스의 메서드를 재정의하는 과정

     

     

    메서드 오버라이딩 조건
    • 메서드 이름 : 메서드 이름이 같아야 한다
    • 메서드 매개변수(파라미터) : 매개변수(파라미터) 타입, 순서, 개수가 같아야 한다
    • 반환 타입 : 반환 타입이 같아야 한다 (단 반환 타입이 하위 클래스 타입일 수 있다)
    • 접근 제어자 : 오버라이딩 메서드의 접근 제어자는 상위 클래스의 메서드보다 더 제한적이어서는 안 된다
    • 예외 : 오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 ` throws` 로 선언할 수 없다 하지만 더 적거나 같은 수의 예외, 또는 하위 타입의 예외는 선언할 수 있다
    • static, final, private : 키워드가 붙은 메서드는 오버라이딩 될 수 없다
    • 생성자 오버라이딩 : 생성자는 오버라이딩 할 수 없다

     

     

    super - 부모 참조

    부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있으면, 자식에서 부모의 필드나 메서드를 호출할 수 없다

    이때 super 키워드를 사용하면 부모를 참조할 수 있다

    super는 이름 그대로 부모 클래스에 대한 참조를 나타낸다

    public class Parent {
        public String value = "parent";
        
        public void hello() {
            System.out.println("Parent.hello");
        }
    }
     
    public class Child extends Parent {
        public String value = "child";
        
        @Override
        public void hello() {
            System.out.println("Child.hello");
        }
        
        public void call() {
            System.out.println("this value = " + this.value); // this 생략 가능
            System.out.println("super value = " + super.value);
            this.hello(); // this 생략 가능
            super.hello();
        }
    }
    public class Super1Main {
       public static void main(String[] args) {
           Child child = new Child();
           child.call();
        }
    }
    
    // this value = child
    // super value = parent
    // Child.hello
    // Parent.hello

    > 필드 이름과 메서드 이름이 같지만 super를 사용해서 부모 클래스에 있는 기능을 사용할 수 있다

     

     

    super - 생성자

    상속 관계의 인스턴스를 생성하면 결국 메모리 내부에는 자식과 부모 클래스가 각각 다 만들어진다

    상속 관계를 사용하면 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다 (규칙)

    // 최상위 부모 클래스
    public class ClassA {
        public ClassA() {
            System.out.println("ClassA 생성자");
        }
    }
    
    public class ClassB extends ClassA {
        public ClassB(int a) {
            super(); // 기본 생성자 생략 가능
            System.out.println("ClassB 생성자 a="+a);
        }
        
        public ClassB(int a, int b) {
            super(); // 기본 생성자 생략 가능
            System.out.println("ClassB 생성자 a="+a + " b=" + b);
        }
    }
    
    public class ClassC extends ClassB {
        public ClassC() {
            // 생성자는 하나만 호출할 수 있다 두 생성자 중에 하나를 선택하면 된다
            // ClassC에는 기본 생성자가 없다 따라서 부모의 기본 생성자를 호출하는 super()를 사용하거나 생략할 수 없다
            super(10, 20);
            System.out.println("ClassC 생성자");
        }
    }

     

     

     

    10. 다형성 - 1

    다형성 시작

    객체지향 프로그래밍의 대표적인 특징으로는 캡슐화, 상속, 다형성이 있다

    그중에서 다형성은 객체지향 프로그래밍의 꽃이라 불린다

     

    다형성(Polymorphism)은 이름 그대로 "다양한 형태", "여러 형태"를 뜻한다

    프로그래밍에서 다형성은 한 객체가 여러 타입의 객체로 취급될 수 있는 능력을 뜻한다

    보통 하나의 객체는 하나의 타 입으로 고정되어 있다

    그런데 다형성을 사용하면 하나의 객체가 다른 타입으로 사용될 수 있다는 뜻

     

     

    다형적 참조

    보통 한 가지 형태만 참조할 수 있다

    • Parent parent = new Parent()
    • Child child = new Child()

     

    부모 타입의 변수가 자식 인스턴스 참조

    Parent 타입의 변수는 자신인 Parent는 물론이고 자식 타입까지 참조할 수 있다

    • Parent poly = new Parent()
    • Parent poly = new Child()
    • Parent poly = new Grandson()

    자바에서 부모 타입은 자신은 물론이고, 자신을 기준으로 모든 자식 타입을 참조할 수 있다

    이것이 바로 다양한 형태를 참조할 수 있다고 해서 다형적 참조라 한다

     

     

    다형적 참조의 한계

    상속 관계는 부모 방향으로 찾아 올라갈 수는 있지만 자식 방향으로 찾아 내려갈 수는 없다

    Parent는 부모 타입이고 상위에 부모가 없다

    따라서 childMethod()를 찾을 수 없으므로 컴파일 오류가 발생한다

    이런 경우 childMethod()를 호출하고 싶으면 캐스팅이 필요하다

     

    다형적 참조의 핵심은 부모는 자식을 품을 수 있다는 것이다

     

     

    다형성과 캐스팅
    public class CastingMain1 {
        public static void main(String[] args) {
            // 부모 변수가 자식 인스턴스 참조(다형적 참조)
            Parent poly = new Child();
        
            // 단 자식의 기능은 호출할 수 없다. 컴파일 오류 발생
            //poly.childMethod();
        
            //다운캐스팅(부모 타입 -> 자식 타입)
            Child child = (Child) poly;
            child.childMethod();
        }
    }

    캐스팅을 한다고 해서 Parent poly의 타입이 변하는 것은 아니다 

    해당 참조값을 꺼내고 꺼낸 참조값이 Child 타입이 되는 것이다

    따라서 poly 의 타입은 Parent로 기존과 같이 유지된다

     

     

    캐스팅
    • 업캐스팅(upcasting) : 부모 타입으로 변경
    • 다운캐스팅(downcasting) : 자식 타입으로 변경

     

     

    일시적 다운 캐스팅

    다운캐스팅 결과를 변수에 담아두는 과정이 번거롭다

    이런 과정 없이 일시적으로 다운캐스팅을 해서 인스턴스에 있는 하위 클래스의 기능을 바로 호출할 수 있다

    public class CastingMain2 {
        public static void main(String[] args) {
            
            // 부모 변수가 자식 인스턴스 참조(다형적 참조)
            Parent poly = new Child();
            
            // 단 자식의 기능은 호출할 수 없다. 컴파일 오류 발생
            // poly.childMethod();
            
            //일시적 다운캐스팅 - 해당 메서드를 호출하는 순간만 다운캐스팅
            ((Child) poly).childMethod();
        }
    }

     

     

    업캐스팅
    public class CastingMain3 {
        public static void main(String[] args) {
            Child child = new Child();
            
            Parent parent1 = (Parent) child; // 업캐스팅은 생략 가능, 생략 권장
            Parent parent2 = child; // 업캐스팅 생략
            parent1.parentMethod();
            parent2.parentMethod();
        }
    }

    업캐스팅은 생략할 수 있지만 다운캐스팅은 생략할 수 없다

    참고로 업캐스팅은 매우 자주 사용하기 때문에 생략을 권장한다

    자바에서 부모는 자식을 담을 수 있고 그 반대는 안된다 꼭 필요하다면 다운캐스팅을 해야 한다

     

     

    다운캐스팅과 주의점

    다운캐스팅은 잘못하면 심각한 런타임 오류가 발생할 수 있다

    public class CastingMain4 {
        public static void main(String[] args) {
        
            Parent parent1 = new Child();
            Child child1 = (Child) parent1;
            child1.childMethod(); // 문제 없음
            
            Parent parent2 = new Parent();
            Child child2 = (Child) parent2; // 런타임 오류 - ClassCastException
            child2.childMethod(); // 실행 불가
        }
    }

    먼저 new Parent()로 부모 타입으로 객체를 생성한다 따라서 메모리 상에 자식 타입은 전혀 존재하지 않는다

    다음으로 parent2를 Child 타입으로 다운캐스팅한다

    그런데 parent2는 Parent로 생성이 되었기 때문에 메모리 상에 Child가 존재하지 않는다

    따라서 Child 자체를 사용할 수 없다

     

     

    instance of

    변수가 참조하는 인스턴스의 타입을 확인하고 싶다면 instance of 키워드를 사용하면 된다

     

     

    11. 다형성 - 2

    추상 클래스

    추상 클래스는 이름 그대로 추상적인 개념을 제공하는 클래스이다

    따라서 실체인 인스턴스가 존재하지 않는다

    대신에 상속을 목적으로 사용되고, 부모 클래스 역할을 담당한다

     abstract class AbstractAnimal {...}
    • 추상 클래스는 클래스를 선언할 때 앞에 추상이라는 의미의 abstract 키워드를 붙여주면 된다
    • 추상 클래스는 기존 클래스와 완전히 같다 
    • 다만 new AbstractAnimal() 와 같이 직접 인스턴스를 생성하지 못하는 제약이 추가된 것

     

     

    추상 메서드

    부모 클래스를 상속 받는 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에 정의

    추상 메서드는 이름 그대로 추상적인 개념을 제공하는 메서드

    따라서 실체가 존재하지 않고, 메서드 바디가 없다

     public abstract void sound();
    • 추상 메서드는 선언할 때 메서드 앞에 추상이라는 의미의 abstract 키워드를 붙여주면 된다
    • 추상 메서드가 하나라도 있는 클래스는 추상 클래스로 선언해야 한다
    • 추상 메서드는 메서드 바디가 없다 따라서 작동하지 않는 메서드를 가진 불완전한 클래스로 볼 수 있다 > 직접 생성하지 못하도록 추상 클래스로 선언해야 한다
    • 추상 메서드는 상속 받는 자식 클래스가 반드시 오버라이딩 해서 사용해야 한다

     

     

    public abstract class AbstractAnimal {
        public abstract void sound();
        
        public void move() {
            System.out.println("동물이 움직입니다.");
        }
    }

     

    • AbstractAnimal은 abstract가 붙은 추상 클래스, 이 클래스는 직접 인스턴스를 생성할 수 없다
    • sound()는 abstract가 붙은 추상 클래스, 이 메서드는 자식이 반드시 오버라이딩 해야 한다
    • move() 드는 추상 메서드가 아니다 따라서 자식 클래스가 오버라이딩 하지 않아도 된다

     

     

    public class Dog extends AbstractAnimal {
        @Override
        public void sound() {
            System.out.println("멍멍");
        }
    }
    public class AbstractMain {
        public static void main(String[] args) {
            // 추상클래스 생성 불가
            // AbstractAnimal animal = new AbstractAnimal();
            // java: poly.ex3.AbstractAnimal is abstract; cannot be instantiated
            
            Dog dog = new Dog();
            dog.sound();
            dog.move();
            soundAnimal(dog);
        }
        
        // 동물이 추가 되어도 변하지 않는 코드
        private static void soundAnimal(AbstractAnimal animal) {
            System.out.println("동물 소리 테스트 시작");
            animal.sound();
            System.out.println("동물 소리 테스트 종료");
        }
    }

     

     

    순수 추상 클래스

    모든 메서드가 추상 메서드인 추상 클래스

    모든 메서드가 추상 메서드인 순수 추상 클래스는 코드를 실행할 바디 부분이 전혀 없다

    단지 다형성을 위한 부모 타입으로써 껍데기 역할만 제공

    public abstract class AbstractAnimal {
        public abstract void sound();
        public abstract void move();
    }
    public class Dog extends AbstractAnimal {
        @Override
        public void sound() {
            System.out.println("멍멍");
        }
        
        @Override
        public void move() {
            System.out.println("개 이동");
        }
    }
    public class AbstractMain {
        public static void main(String[] args) {
            Dog dog = new Dog();
            soundAnimal(dog);
            moveAnimal(dog);
        }
        
        // 동물이 추가 되어도 변하지 않는 코드
        private static void soundAnimal(AbstractAnimal animal) {
            System.out.println("동물 소리 테스트 시작");
            animal.sound();
            System.out.println("동물 소리 테스트 종료");
        }
        
        // 동물이 추가 되어도 변하지 않는 코드
        private static void moveAnimal(AbstractAnimal animal) {
            System.out.println("동물 이동 테스트 시작");
            animal.move();
            System.out.println("동물 이동 테스트 종료");
        }
    }

     

     

    인터페이스

    자바는 순수 추상 클래스를 더 편리하게 사용할 수 있는 인터페이스라는 기능을 제공한다

    public interface InterfaceAnimal {
        // 인터페이스 - public abstract 키워드 생략 가능
        public abstract void sound();
        public abstract void move();
    }

    인터페이스는 다중 구현(다중 상속)을 지원한다

     

     

    인터페이스와 멤버 변수
    public interface InterfaceAnimal {
        public static final double MY_PI = 3.14;
    }
    
    public interface InterfaceAnimal {
        // 생략 권장
        double MY_PI = 3.14;
    }

    인터페이스에서 멤버 변수는 public, static, final이 모두 포함되었다고 간주한다

     

     

    public interface InterfaceAnimal {
        void sound();
        void move();
    }

    sound(), move() 앞에 public abstract가 생략되어있다

    따라서 상속 받을 곳에서 모든 메서드를 오버라이딩 해야 한다

     

     

    public class Dog implements InterfaceAnimal {   
        @Override
        public void sound() {
            System.out.println("멍멍");
        }
        
        @Override
        public void move() {
            System.out.println("개 이동");
        }
    }

    인터페이스는 상속이라 하지 않고 구현이라 한다

     

     

    public class InterfaceMain {
        public static void main(String[] args) {
        // 인터페이스 생성 불가
        // InterfaceAnimal interfaceMain1 = new InterfaceAnimal();
     
        Dog dog = new Dog();
        soundAnimal(dog);
        }
        
        // 동물이 추가 되어도 변하지 않는 코드
        private static void soundAnimal(InterfaceAnimal animal) {
            System.out.println("동물 소리 테스트 시작");
            animal.sound();
            System.out.println("동물 소리 테스트 종료");
        }
    }

     

     

    상속 vs 구현

    부모 클래스의 기능을 자식 클래스가 상속 받을 때, 클래스는 상속 받는다고 표현하지만, 부모 인터페이스의 기능을 자식이 상속 받을 때는 인터페이스를 구현한다고 표현한다

    상속은 이름 그대로 부모의 기능을 물려 받는 것

    인터페이스는 메서드 이름만 있는 설계도이고, 이 설계도가 실제 어떻게 작동하는지는 하위 클래스에서 모두 구현하는 것

     

     

    인터페이스를 사용해야 하는 이유
    • 제약 : 인터페이스를 만드는 이유는 인터페이스를 구현하는 곳에서 인터페이스의 메서드를 반드시 구현해라는 규 약(제약)을 주는 것이다
    • 다중 구현 : 자바에서 클래스 상속은 부모를 하나만 지정할 수 있다 반면에 인터페이스는 부모를 여러명 두는 다중 구현(다중 상속)이 가능하다

     

     

    인터페이스 - 다중 구현
    public interface InterfaceA {
        void methodA();
        void methodCommon();
    }
    
    public interface InterfaceB {
        void methodB();
        void methodCommon();
    }
    public class Child implements InterfaceA, InterfaceB {
        @Override
        public void methodA() {
            System.out.println("Child.methodA");
        }
        
        @Override
        public void methodB() {
            System.out.println("Child.methodB");
        }
        
        @Override
        public void methodCommon() {
            System.out.println("Child.methodCommon");
        }
    }

    implements InterfaceA, InterfaceB와 같이 다중 구현을 할 수 있다

    implements 키워드 뒤에 ,로 여러 인터페이스를 구분하면 된다

     

     

    public class DiamondMain {
        public static void main(String[] args) {
            InterfaceA a = new Child();
            a.methodA();
            a.methodCommon();
            
            InterfaceB b = new Child();
            b.methodB();
            b.methodCommon();
        }
    }

     

     

    클래스와 인터페이스 활용
    • AbstractAnimal은 추상 클래스다
    • sound() : 동물의 소리를 내기 위한 sound() 추상 메서드를 제공
    • move() : 동물의 이동을 표현하기 위한 메서드, 상속을 목적으로 사용
    • Fly는 인터페이스
    public abstract class AbstractAnimal {
        public abstract void sound();
        public void move() {
            System.out.println("동물이 이동합니다.");
        }
    }
    public interface Fly {
        void fly();
    }
    public class Dog extends AbstractAnimal {
        @Override
        public void sound() {
            System.out.println("멍멍");
        }
    }
    public class Bird extends AbstractAnimal implements Fly {
        @Override
        public void sound() {
            System.out.println("짹짹");
        }
        
        @Override
        public void fly() {
            System.out.println("새 날기");
        }
    }

     

     

    cf. 하나의 클래스 여러 인터페이스 예시

    public class Bird extends AbstractAnimal implements Fly, Swim {

    extends 를 통한 상속은 하나만 할 수 있고 implements를 통한 인터페이스는 다중 구현 할 수 있기 때문에 둘이 함께 나온 경우 extends가 먼저 나와야 한다

     

     

    public class SoundFlyMain {
        public static void main(String[] args) {
            Dog dog = new Dog();
            Bird bird = new Bird();
            soundAnimal(dog);
            soundAnimal(bird);
            flyAnimal(bird);
        }
        
        // AbstractAnimal 사용 가능
        private static void soundAnimal(AbstractAnimal animal) {
            System.out.println("동물 소리 테스트 시작");
            animal.sound();
            System.out.println("동물 소리 테스트 종료");
        }
        
        //Fly 인터페이스가 있으면 사용 가능
        private static void flyAnimal(Fly fly) {
            System.out.println("날기 테스트 시작");
            fly.fly();
            System.out.println("날기 테스트 종료");
        }
    }

     

     

     

    12. 다형성과 설계

    객체 지향 특징
    • 추상화
    • 캡슐화
    • 상속
    • 다형성

     

     

    객체 지향 프로그래밍

    객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위,

    객체들의 모임으로 파악하고자 하는 것이다

    각각의 객체는 메세지를 주고받고, 데이터를 처리할 수 있다

    객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하기 만들기 때문에 대규모 소프트웨어 개발에서 많이 사용한다

     

     

    다형성 - 역할과 구현을 분리
    • 역할과 구현으로 구분하면 세상이 단순해지고 유연해지며 변경도 편리하다
    • 장점 1 : 클라이언트는 대상의 역할(인터페이스)만 알면 된다
    • 장점 2 : 클라이언트는 구현 대상의 내부 구조를 몰라도 된다
    • 장점 3 : 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향 받지 않는다
    • 장점 4 : 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다 

     

     

    자바 언어 - 역할과 구현의 분리
    • 자바 언어의 다형성을 활용 (역할=인터페이스, 구현=인터페이스를 구현한 클래스, 구현 객체)
    • 객체를 설계할 때 역할과 구현을 명확히 분리
    • 객체 설계시 역할(인터페이스)를 먼저 부여하고 그 역할을 수행하는 구현 객체 만들기

     

     

    다형성의 본질
    • 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다
    • 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야 한다
    • 클라이언트를 변경하지 않고 서버의 구현 기능을 유연하게 변경할 수 있다

    > 다형성이 가장 중요하다

    > 디자인 패턴 대부분은 다형성을 활용하는 것이다

    > 스프링의 핵심인 제어의 역전(IoC), 의존관계 주입(DI)도 결국 다형성을 활용하는 것이다

     

     

    Driver : 운전자는 자동차(Car) 의 역할에만 의존한다

    • Driver 클래스는 Car car 멤버 변수를 가진다 따라서 Car 인터페이스를 참조한다
    • 인터페이스를 구현한 K3Car, Model3Car에 의존하지 않고 Car 인터페이스에만 의존한다

    Car : 자동차 역할이고 인터페이스이다 K3Car, Model3Ca 클래스가 인터페이스를 구현한다

    public interface Car {
        void startEngine();
        void offEngine();
        void pressAccelerator();
    }
    public class K3Car implements Car {
        @Override
        public void startEngine() {
            System.out.println("K3Car.startEngine");
        }
        
        @Override
        public void offEngine() {
            System.out.println("K3Car.offEngine");
        }
        
        @Override
        public void pressAccelerator() {
            System.out.println("K3Car.pressAccelerator");
        }
    }
    public class Model3Car implements Car {
        @Override
        public void startEngine() {
            System.out.println("Model3Car.startEngine");
        }
        
        @Override
        public void offEngine() {
            System.out.println("Model3Car.offEngine");
        }
        
        @Override
        public void pressAccelerator() {
            System.out.println("Model3Car.pressAccelerator");
        }
    }
    public class Driver {
    
        private Car car;
        
        public void setCar(Car car) {
            System.out.println("자동차를 설정합니다: " + car);
            this.car = car;
        }
        
        public void drive() {
            System.out.println("자동차를 운전합니다.");
            car.startEngine();
            car.pressAccelerator();
            car.offEngine();
        }
    }
    /**
     * 다형성을 활용한 런타임 변경
     * 런타임: 애플리케이션 실행 도중에 변경 가능
    */
    public class CarMain1 {
        public static void main(String[] args) {
        
            Driver driver = new Driver();
            // 차량 선택(k3)
            Car k3Car = new K3Car();
            driver.setCar(k3Car);
            driver.drive();
            
            // 차량 변경(k3 -> model3)
            Car model3Car = new Model3Car();
            driver.setCar(model3Car);
            driver.drive();
        }
    }

     

     

    OCP(Open-Closed Principle) 원칙

    좋은 객체 지향 설계 원칙 중 하나로 OCP 원칙이라는 것이 있다

    • Open for extension : 새로운 기능의 추가나 변경 사항이 생겼을 때, 기존 코드는 확장할 수 있어야 한다
    • Closed for modification : 기존의 코드는 수정되지 않아야 한다

    > Car를 사용하는 클라이언트 코드인 Driver 코드의 변경없이 새로운 자동차를 확장할 수 있다

    > 다형성을 활용하고 역할과 구현을 분리한 덕분에 새로운 자동차를 추가해도 대부분의 핵심 코드들을 그대로 유지할 수 있게 되었다

     

     

     

     

     


    김영한의 실전자바 - 기본편 (9강 - 12강)

    728x90
Designed by Tistory.