TIL

8/16

iksadnorth 2023. 8. 16. 13:37

👣 개요

  • 클래스
    • OOP 이해하기
    • 클래스 설계
    • 객체 생성과 참조형 변수
    • 필드
    • 메서드
    • 인스턴스 멤버와 클래스 멤버
    • 생성자
    • this
    • 접근 제어자
    • package, import
  • 상속
    • 클래스 간 관계와 상속
    • 오버라이딩
    • 다형성
    • 추상 클래스
  • 인터페이스
    • 역할
    • 디폴트 메서드, static 메서드
    • 다형성
  • 3주차 숙제 - 계산기 만들기

 

👣 OOP의 특징 - PEAI

1. 다형성 - Polymorphism

더보기

특정 타입의 변수에 다양한 타입의 객체를 부여할 수 있는 특징.
이렇게 함으로서 코드 유연성과 재활용성을 높일 수 있다.

키포인트는 하나의 타입에 여러 타입을 대입할 수 있다는 것이다.

// 다형성이 적용된 때,
Coffee coffee = new Espresso();
Coffee coffee = new Americano();
Coffee coffee = new Latte();

// 실제로 사용하는 코드
void useCoffee(Coffee coffee) {
    coffee.drink();
    coffee.pourInto(sink);
}
// 다형성이 적용되지 않을 때,
Espresso coffee = new Espresso();
Americano coffee = new Americano();
Latte coffee = new Latte();

// 실제로 사용하는 코드
// 커피 종류가 바뀔 때마다 코드를 만들어야 한다.
void useCoffee(Espresso coffee) {
    coffee.drink();
    coffee.pourInto(sink);
}

void useCoffee(Americano coffee) {
    coffee.drink();
    coffee.pourInto(sink);
}


void useCoffee(Latte espresso) {
    coffee.drink();
    coffee.pourInto(sink);
}

2. 캡슐화 - Encapsulation

더보기

객체를 기준으로 밖에서 안쪽의 데이터를 함부로 조작하지 못하게 하는 것.
무단으로 조작을 못하게 하는 대신 객체가 허용한 방식으로만 데이터를 조작하는 방식.

// 캡슐화를 이용한 코드
public class Car {
    private int speed;  // 속도를 private으로 설정

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int newSpeed) {
        if (newSpeed >= 0 && newSpeed <= 200) {  // 유효한 범위 내에서만 속도 변경
            speed = newSpeed;
        } else {
            System.out.println("속도 범위를 벗어났습니다.");
        }
    }
}

// 실제로 사용하는 코드
public class CarSimulation {
    public static void main(String[] args) {
        Car myCar = new Car();
        
        myCar.speed = -120;   // 유효하지 않는 방식으로 변경 - 통제 불가능 
        
        myCar.setSpeed(-120);  // 유효한 방식으로 속도 변경 - 통제가 가능
    }
}

위 방식처럼 객체의 필드에 직접 접근하면 설계자가 원하는 방식으로 데이터를 통제할 수 없다.
자동차의 속도는 마이너스 값을 가지면 안된다. 하지만 필드로 접근하면 마이너스 속도값을 가질 수 있다.
하지만 필드에 private 제한을 걸고 메서드로 값을 바꾸게 우회시키면 음수값이 들어오면 적절하게 통제할 수 있다.

3. 추상화 - Abstraction

더보기

객체의 공통된 부분을 모아 상위 개념으로 새롭게 선언할 수 있다는 개념.
해당 과정을 통해 코드 유지보수성을 크게 높일 수 있다.

예를 들자면, 동생에게 은행가서 10만원을 뽑아달라고 했을 때,
동생이 우리은행에서 뽑을까? 아니면 신한은행? 국민은행? 라고 물어보면  화가 머리끝까지 날 것이다.
은행에서 10만원을 뽑는 것이 중요한 것이지
신한은행에서 김미영 팀장에게 부탁해서 10만원을 뽑을 것까지 고려할 필요는 없기 때문이다.

코드에서도 마찬가지다. 딱 필요한 공통 부분만 요구를 할 뿐 구체적인 구현 상황에 대해 신경을 쓸 필요가 없기 때문에 각 구현체의 상위개념인 은행으로 추상화해 추상화된 요구만 할 뿐이다.

// 추상화가 적용된 경우.
interface Bank {
    void draw(int amount);
    int consultAboutLoan();
}

class WooriBank extends Bank {
    @override
    void draw(int amount) {
        // 우리 은행의 인출 방식.
    }
    @override
    int consultAboutLoan() {
        // 우리 은행의 대출 상담 결과.
    }
}

class ShinhanBank extends Bank {
    @override
    void draw(int amount) {
        // 신한 은행의 인출 방식.
    }
    @override
    int consultAboutLoan() {
        // 신한 은행의 대출 상담 결과.
    }
}

// 다형성까지 사용한 결과.
Bank bank = new WooriBank();
// Bank bank = new ShinhanBank();

bank.draw(10);
bank.consultAboutLoan

4. 상속 - Inheritance

더보기

새로운 클래스를 생성할 때, 특정 클래스의 몇몇 부분만 수정하고 싶다면 
해당 클래스를 상속받아 대부분의 기능을 복사하는 것.

// 부모 클래스
class Animal {
    String name;
    
    Animal(String name) {
        this.name = name;
    }
    
    void eat() {
        System.out.println(name + " is eating.");
    }
}

// 자식 클래스
class Bird extends Animal {
    Bird(String name) {
        super(name); // 부모 클래스 생성자 호출
    }
    
    void fly() {
        System.out.println(name + " is flying.");
    }
}

public class InheritanceExample {
    public static void main(String[] args) {
        Bird sparrow = new Bird("Sparrow");
        
        sparrow.eat(); // 부모 클래스 메서드 호출
        sparrow.fly(); // 자식 클래스 메서드 호출
    }
}

 

👣 OOP의 원칙 - SOLID

1. SRP - Single Responsibility Principle

클래스는 1개의 책임만 가져야 한다.
예를 들어서 Car 클래스는 '탑승자를 태워준다'라는 기능만 존재하면 되지 '잠자리를 제공한다.', '음식을 보관한다'와 같은 기능까지 내포하지 않아야 한다는 뜻이다. 만약 이 원칙을 지키지 않는다면 Test를 수행하기 어려울 뿐만 아니라 유지보수성도 떨어진다.

2. OCP - Open Close Principle

확장에는 열려 있지만 수정에는 닫혀 있어야 한다.
예를 들어, Galaxy S22의 새로운 기능을 개발했을 때,
Galaxy S22에 업데이트[수정]하는 것이 아닌 Galaxy S23에 해당 기능을 추가하는 것[확장]이라고 생각하면 된다.

굳이 새로운 버전에 넣는 이유는 기존의 것에 업데이트를 하게 되면 이미 해당 버전을 사용하는 즉, S22를 사용하던 유저들은 해당 기능을 원하지 않거나 업데이트 이전의 기능을 생업에 이용하고 있으면 그 업데이트로 인해 큰 혼란을 빚어낼 수 있기 때문에 신 버전과 구 버전을 공존할 수 있게 확장을 하는 것이다.

3. LSP - Liscov Substitution Principle

상속 관계에서의 하위 클래스는 상위 클래스를 완전 대체가능해야 한다.
예를 들어, Bank 클래스를 상속받은 WooriBank 클래스와 ShinhanBank 클래스 모두 현금 인출이라는 기능을 가지고 있어야 한다.
만약 SanWaMoney 클래스가 Bank를 상속받았는데 현금 인출이라는 기능이 없다면
Bank 클래스를 상속받는 것이 아니라 LoanShark 클래스를 상속받아야 한다.

4. ISP - Interface Sergregation Principle

인터페이스는 지나치게 광범위하거나 지나치게 많은 기능을 구현해서는 안 된다.
예를 들어, Bank 인터페이스가 '현금 인출 기능'과 '대출 기능' '주식 매매 기능'라는 기능을 가지고 있다고 가정하자.
우리 은행에서는 '현금 인출 기능'과 '대출 기능'을 제공하지만 '주식 매매 기능'을 제공하진 않는다.

하지만 Bank 인터페이스가 저 기능을 유지한다면 우리 은행은 어쩔 수 없이 '주식 매매 기능'을 불필요하게 구현해야 한다.
위 같은 방식은 상당히 억지스럽고 혼동을 빚어 내므로 차라리 Bank 인터페이스를 다음과 같이 분리해야 한다.

// ISP 원칙 위반 예시.
interface Bank {
    void draw();
    void loan();
    void sellStock();
}

// ISP 원칙 위반 예시.
interface Bank { // 은행 인터페이스
    void draw();
    void loan();
}
interface BrokerageFirm { // 증권사 인터페이스
    void sellStock();
}

5. DIP - Dependency Inversion Principle

고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 된다.
예를 들어, 사단장이 GOP에 찾아와서 경계 강화를 위해 지시를 내릴 생각을 가지고 있다고 가정하자.

이 때, 사단장이 GOP 소대장에게 말하길, '내가 GOP 소초장이었을 때는 말이야, 모든 인원이 모든 초소에 투입되어 전방 경계와 후방 경계를 각각 사수와 부사수가 맡게 했어. 그리고 크레모아는 실수로 격발되지 못하게 연결선을 끊어 놓고 격발기 방아쇠에 스펀지를 끼워넣어 2중으로 안전 장치를 구비해뒀어. 그리고 ............. 그러니까 이런 방식으로 경계를 강화해 알았지?' 라고 지시를 내렸다고 생각해보자. 이런 방식은 너무 구식일 뿐만 아니라 각 소초마다 상황도 다르다.
때문에 해당 방식은 유지보수에 취약하고 작동방식도 너무 불편하다.

만약 사단장이 GOP 소대장에게 위와 같이 말하는 것이 아니라
그 위의 직책의 작전과장에게 '요즘 도발 위험이 높아졌으니 경계 작전 수위를 높여'라고
추상화된 명령을 내리게 한다면, 해당 작전과장은 각 소초에 맞게 작전을 알아서 계획하고 각 중대에 전파할 것이다.
이러면 각 소초에 맞는 그리고 시대 상황에 맞는 명령을 내릴 수 있게 된다.

이처럼 고수준의 모듈[사단장]은 지나치게 낮은 수준의 모듈[소대장]에게 의존하는 것이 아니라
좀더 윗 수준의 모듈[작전과장]에게 의존해서 유지보수성을 높일 수 있다.

 

👣 hashCode() vs equals()

hash 값을 사용하는 Collection(HashMap, HashSet, HashTable)은
객체가 논리적으로 같은지 비교할 때 위 그림과 같은 과정을 거친다.

때문에 객체의 동등성을 비교할 때,
equals() 뿐 아니라 hashCode()도 재정의해야 동등한 객체 취급 받는다.

HE로 확인한다고 생각하면된다.

 

👣 3주차 숙제 - 계산기 만들기

 

GitHub - iksadNorth/homework-week-3

Contribute to iksadNorth/homework-week-3 development by creating an account on GitHub.

github.com

 

'TIL' 카테고리의 다른 글

8/19  (0) 2023.08.19
8/18  (0) 2023.08.18
8/17  (0) 2023.08.17
8/15  (0) 2023.08.15
8/14  (0) 2023.08.14