๐ŸŽฏ 1์ฃผ์ฐจ Unit 2.4 โ€” ๋‹คํ˜•์„ฑ (Polymorphism) โ˜…โ˜…โ˜…

Psjยท3์ผ ์ „

F-lab

๋ชฉ๋ก ๋ณด๊ธฐ
26/42

๐ŸŽฏ Unit 2.4 โ€” ๋‹คํ˜•์„ฑ (Polymorphism) โ˜…โ˜…โ˜…

F-lab Java 1์ฃผ์ฐจ / Phase 2 / Unit 2.4 ๋ณธ๊ฒฉ ํ•™์Šต ์ž๋ฃŒ
9-์„น์…˜ ๋งˆ์Šคํ„ฐ ํ”„๋กฌํ”„ํŠธ ํ˜•์‹์œผ๋กœ ๊นŠ์ด ํŒŒํ—ค์นœ๋‹ค.

์„ ์ˆ˜ ์ง€์‹: Unit 2.3 (์ƒ์†๊ณผ ์ƒ์„ฑ์ž ์ฒด์ด๋‹)
๋‹ค์Œ Unit: 2.5 โ€” instanceof์™€ ํ˜•๋ณ€ํ™˜

์ด Unit์˜ ์˜๋ฏธ: OOP 4๋Œ€ ์›์น™์˜ ์ •์ . ๋ฉด์ ‘์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ๋ฌป๋Š” ์˜์—ญ.
๋‹คํ˜•์„ฑ์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๋ฉด Spring DI, JPA, Strategy ํŒจํ„ด ๋“ฑ ๋ชจ๋“  ํ˜„๋Œ€ ์ž๋ฐ”์˜ ํ† ๋Œ€ ๊ฐ€ ์žกํžŒ๋‹ค.


๐ŸŒ 1. ์„ธ์ƒ ์† ๋น„์œ 

๋‹คํ˜•์„ฑ = "๊ฐ™์€ ๋ช…๋ น, ๋‹ค๋ฅธ ๋ฐ˜์‘"

์„ ์ƒ๋‹˜์ด ๊ต์‹ค์—์„œ ์™ธ์นฉ๋‹ˆ๋‹ค:

"๋ชจ๋‘ ์ธ์‚ฌํ•˜์„ธ์š”!"

ํ•™์ƒ๋“ค์˜ ๋ฐ˜์‘:

  • ๋ฏผ์ˆ˜ (ํ•œ๊ตญ์ธ): "์•ˆ๋…•ํ•˜์„ธ์š”" + 90๋„ ์ธ์‚ฌ
  • John (๋ฏธ๊ตญ์ธ): "Hello!" + ์† ํ”๋“ค๊ธฐ
  • Yuki (์ผ๋ณธ์ธ): "ใ“ใ‚“ใซใกใฏ" + ๊นŠ์€ ์ ˆ
  • Ahmed (์•„๋ž์ธ): "ุงู„ุณู„ุงู… ุนู„ูŠูƒู…" + ๊ฐ€์Šด์— ์†

๊ฐ™์€ ๋ช…๋ น ("์ธ์‚ฌํ•˜์„ธ์š”") ์ธ๋ฐ ๋‹ค๋ฅธ ๋ฐ˜์‘ ์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ:

  • ์„ ์ƒ๋‹˜์€ ํ•™์ƒ๋“ค์˜ ๊ตญ์ ์„ ์ผ์ผ์ด ํ™•์ธํ•˜์ง€ ์•Š์Œ
  • ๊ทธ๋ƒฅ "์ธ์‚ฌํ•˜์„ธ์š”" ๋ผ๊ณ ๋งŒ ํ•จ
  • ๊ฐ ํ•™์ƒ์ด ์ž๊ธฐ ๋ฐฉ์‹๋Œ€๋กœ ์ธ์‚ฌ

โ†’ ์ด๊ฒŒ ๋‹คํ˜•์„ฑ โ€” ๊ฐ™์€ ๋ฉ”์‹œ์ง€์— ๊ฐ์ฒด๋งˆ๋‹ค ๋‹ค๋ฅธ ์‘๋‹ต.


๋” ์ผ์ƒ์ ์ธ ๋น„์œ  โ€” "TV ๋ฆฌ๋ชจ์ปจ"

๋‹น์‹ ์ด ํ˜ธํ…” ๋ฐฉ์˜ ๋ฆฌ๋ชจ์ปจ์„ ๋ด…๋‹ˆ๋‹ค. ๋ฆฌ๋ชจ์ปจ์—๋Š” "์ „์›" ๋ฒ„ํŠผ ์ด ์žˆ์–ด์š”.

์ด ํ˜ธํ…”์—๋Š” ๋‹ค์–‘ํ•œ TV๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • LG TV
  • ์‚ผ์„ฑ TV
  • ์†Œ๋‹ˆ TV
  • ์˜›๋‚  ๋ธŒ๋ผ์šด๊ด€ TV

๋‹น์‹ ์ด "์ „์›" ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด:

  • ์–ด๋–ค TV๋“  ์ผœ์ง€๊ฑฐ๋‚˜ ๊บผ์ง
  • ๋‚ด๋ถ€ ์ž‘๋™์€ ๋‹ค ๋‹ค๋ฅด์ง€๋งŒ ๊ฒฐ๊ณผ๋Š” ๊ฐ™์Œ

๋‹น์‹ ์ด TV ํšŒ์‚ฌ๋ฅผ ์•Œ์•„์•ผ ํ•˜๋‚˜์š”? ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๋ˆ„๋ฅด๋ฉด ๋จ.

๋‹น์‹ : ๋ฆฌ๋ชจ์ปจ.์ „์›()  // โ† ๊ฐ™์€ ๋ช…๋ น
        โ†“
TV๊ฐ€ ์ž๊ธฐ ๋ฐฉ์‹๋Œ€๋กœ ์ฒ˜๋ฆฌ:
- LG: LCD ๋ฐฉ์‹์œผ๋กœ ์ผœ์ง
- ์‚ผ์„ฑ: OLED ๋ฐฉ์‹์œผ๋กœ ์ผœ์ง
- ์†Œ๋‹ˆ: HDR ๋ฐฉ์‹์œผ๋กœ ์ผœ์ง
- ๋ธŒ๋ผ์šด๊ด€: ์ง„๊ณต๊ด€ ๋ฐฉ์‹์œผ๋กœ ์ผœ์ง

โ†’ ๋‹คํ˜•์„ฑ์˜ ๋ณธ์งˆ.


ํ•ต์‹ฌ ์ •๋ฆฌ โ€” "ํ•œ ๋‹จ์–ด, ์—ฌ๋Ÿฌ ์˜๋ฏธ"

๋‹คํ˜•์„ฑ(Polymorphism) = ๊ทธ๋ฆฌ์Šค์–ด ์–ด์›

  • Poly (ํด๋ฆฌ) = "์—ฌ๋Ÿฌ"
  • Morph (๋ชจํ”„) = "ํ˜•ํƒœ"
  • โ†’ "์—ฌ๋Ÿฌ ํ˜•ํƒœ"

์ž๋ฐ”์—์„œ:

"๊ฐ™์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ๊ฐ์ฒด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜๋Š” ๊ฒƒ"

๋น„์œ  ์š”์†Œ์ž๋ฐ” ๋‹คํ˜•์„ฑ
"์ธ์‚ฌํ•˜์„ธ์š”" ๋ช…๋ น๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
ํ•™์ƒ๋“ค๋‹ค์–‘ํ•œ ๊ฐ์ฒด
ํ•œ๊ตญ์‹/๋ฏธ๊ตญ์‹/์ผ๋ณธ์‹ ์ธ์‚ฌ์˜ค๋ฒ„๋ผ์ด๋“œ๋œ ๋ฉ”์„œ๋“œ
์„ ์ƒ๋‹˜์ด ๊ตญ์  ๋ชจ๋ฆ„์ปดํŒŒ์ผ ์‹œ ํƒ€์ž… ๋ชจ๋ฆ„
ํ•™์ƒ์ด ์•Œ์•„์„œ ์ธ์‚ฌ๋Ÿฐํƒ€์ž„์— ๊ฐ์ฒด๊ฐ€ ๊ฒฐ์ •

๐Ÿ”ฅ 2. ํƒ„์ƒ ๋ฐฐ๊ฒฝ

๋‹คํ˜•์„ฑ ์—†์ด โ€” if ์ง€์˜ฅ์˜ ์‹œ๋Œ€

์ƒ์†(Unit 2.3) ๋งŒ ์žˆ๊ณ  ๋‹คํ˜•์„ฑ์ด ์—†๋‹ค๋ฉด, ๊ฐ์ฒด๋ณ„ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ ค๋ฉด ๋งค๋ฒˆ if ๋ถ„๊ธฐ๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// ๋‹คํ˜•์„ฑ ์—†์ด โ€” ๋งค๋ฒˆ ํƒ€์ž… ํ™•์ธ
public void printSound(Animal animal) {
    if (animal instanceof Dog) {
        Dog dog = (Dog) animal;
        System.out.println(dog.bark());
    } else if (animal instanceof Cat) {
        Cat cat = (Cat) animal;
        System.out.println(cat.meow());
    } else if (animal instanceof Cow) {
        Cow cow = (Cow) animal;
        System.out.println(cow.moo());
    } else if (animal instanceof Duck) {
        Duck duck = (Duck) animal;
        System.out.println(duck.quack());
    }
    // ์ƒˆ ๋™๋ฌผ ์ถ”๊ฐ€ ์‹œ ๋˜ else if โŒ
}

๋ฌธ์ œ:

  • ์ƒˆ ๋™๋ฌผ (Pig) ์ถ”๊ฐ€ ์‹œ โ†’ ๋ชจ๋“  ํ•จ์ˆ˜ ์ˆ˜์ •
  • 100๊ฐœ ๋™๋ฌผ ์ข…๋ฅ˜ โ†’ 100๊ฐœ ๋ถ„๊ธฐ
  • ์ƒˆ ๋™๋ฌผ์˜ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ (oink()) ๊นŒ์ง€ ์•Œ์•„์•ผ ํ•จ
  • OOP ๊ฐ€ ์•„๋‹ˆ๋ผ ์ ˆ์ฐจ์ง€ํ–ฅ ์œผ๋กœ ํšŒ๊ท€

๋‹คํ˜•์„ฑ์˜ ๋“ฑ์žฅ โ€” Smalltalk-80 (1980)

๊ฐ์ฒด์ง€ํ–ฅ์˜ ๋ณธ๊ฒฉ์  ์ •๋ฆฝ์€ Smalltalk-80 (1980) ์—์„œ ์ด๋ฃจ์–ด์กŒ์Šต๋‹ˆ๋‹ค. Smalltalk๊ฐ€ ๊ฐ•์กฐํ•œ ํ•ต์‹ฌ ๊ฐœ๋…:

"๊ฐ™์€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด, ๋ฐ›๋Š” ๊ฐ์ฒด๊ฐ€ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค"

// ๋‹คํ˜•์„ฑ ํ™œ์šฉ โ€” if ์‚ฌ๋ผ์ง
public void printSound(Animal animal) {
    System.out.println(animal.makeSound());  // โ† ๊ฐ์ฒด๊ฐ€ ์•Œ์•„์„œ!
}

// ์ƒˆ ๋™๋ฌผ ์ถ”๊ฐ€ํ•ด๋„ ์ด ์ฝ”๋“œ๋Š” ์•ˆ ๋ฐ”๋€œ โœ…

ํ•„์š”ํ•œ ๊ฒƒ:

  • ๋ชจ๋“  ๋™๋ฌผ์ด makeSound() ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง (์ธํ„ฐํŽ˜์ด์Šค ํ†ต์ผ)
  • ๊ฐ ๋™๋ฌผ์ด ์ž๊ธฐ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ (์˜ค๋ฒ„๋ผ์ด๋“œ)
  • โ†’ Animal ํƒ€์ž…์œผ๋กœ ๋‹ค๋ฃจ๊ธฐ๋งŒ ํ•˜๋ฉด ๋จ

์ž๋ฐ”์˜ ์ฑ„ํƒ

์ž๋ฐ”๊ฐ€ 1995๋…„ ๋“ฑ์žฅ ์‹œ ๋‹คํ˜•์„ฑ์„ 1๊ธ‰ ์‹œ๋ฏผ์œผ๋กœ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ:

Spring ์ƒํƒœ๊ณ„์˜ ํ† ๋Œ€:

List<UserRepository> repos;  // ์–ด๋–ค ๊ตฌํ˜„์ฒด๋“  OK
NotificationService service;  // ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์•Œ๋ฉด ๋

JPA:

@Entity ํด๋ž˜์Šค๋ฅผ ๋‹คํ˜•์ ์œผ๋กœ ์ฒ˜๋ฆฌ (Inheritance Strategies)

๋””์ž์ธ ํŒจํ„ด:

  • Strategy Pattern
  • Template Method
  • Factory
  • โ†’ ๊ฑฐ์˜ ๋ชจ๋“  GoF ํŒจํ„ด์ด ๋‹คํ˜•์„ฑ ํ™œ์šฉ

โ†’ ํ˜„๋Œ€ ์ž๋ฐ” = ๋‹คํ˜•์„ฑ ์œ„์— ์„ธ์›Œ์ง„ ๊ฑฐ๋Œ€ํ•œ ํƒ‘.


ํ•ต์‹ฌ ํ†ต์ฐฐ โญ

"๋‹คํ˜•์„ฑ์ด ์—†์œผ๋ฉด ๊ฐ์ฒด์ง€ํ–ฅ์€ ์ ˆ์ฐจ์ง€ํ–ฅ๊ณผ ๋‹ค๋ฅผ ๊ฒŒ ์—†๋‹ค."

์บก์Аํ™” + ์ƒ์†๋งŒ ์žˆ์–ด๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ƒˆ ์ข…๋ฅ˜ ์ถ”๊ฐ€ ์‹œ๋งˆ๋‹ค ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

๋‹คํ˜•์„ฑ์ด ์žˆ์–ด์•ผ "๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์•ˆ ๊ฑด๋“œ๋ฆฌ๊ณ  ํ™•์žฅ" ์ด ๊ฐ€๋Šฅ โ€” ์ด๊ฒŒ SOLID์˜ OCP (๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™) ์˜ ํ•ต์‹ฌ.


๐Ÿ’ฃ 3. ์—†์œผ๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ

๋‹คํ˜•์„ฑ์ด ์—†์„ ๋•Œ์˜ ๊ตฌ์ฒด์  ๋ฌธ์ œ๋ฅผ ILIC ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹œ๋‚˜๋ฆฌ์˜ค: ILIC ์šด์ž„ ๋“ฑ๊ธ‰๋ณ„ ํ• ์ธ ์ฒ˜๋ฆฌ

ILIC๊ฐ€ ๋‹ค์–‘ํ•œ ๊ณ ๊ฐ ๋“ฑ๊ธ‰๋ณ„๋กœ ๋‹ค๋ฅธ ํ• ์ธ์œจ์„ ์ ์šฉํ•œ๋‹ค๊ณ  ํ•ฉ์‹œ๋‹ค:

  • ์ผ๋ฐ˜ ๊ณ ๊ฐ (NORMAL): ํ• ์ธ ์—†์Œ
  • VIP ๊ณ ๊ฐ: 20% ํ• ์ธ
  • ํŒŒํŠธ๋„ˆ (PARTNER): 30% ํ• ์ธ
  • ํ•™์ƒ (STUDENT): 15% ํ• ์ธ

๋‹คํ˜•์„ฑ ์—†์ด โ€” if ์ง€์˜ฅ โŒ

public class Customer {
    private String level;  // "NORMAL", "VIP", "PARTNER", "STUDENT"
    private String name;
    private String email;
    
    public String getLevel() { return level; }
}

public class FareService {
    
    // ํ• ์ธ ๊ณ„์‚ฐ โ€” if ์ง€์˜ฅ
    public int calculateDiscount(Customer customer, int amount) {
        if (customer.getLevel().equals("NORMAL")) {
            return 0;
        } else if (customer.getLevel().equals("VIP")) {
            return amount * 20 / 100;
        } else if (customer.getLevel().equals("PARTNER")) {
            return amount * 30 / 100;
        } else if (customer.getLevel().equals("STUDENT")) {
            return amount * 15 / 100;
        }
        return 0;
    }
    
    // ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€ โ€” ๋˜ if ์ง€์˜ฅ
    public String getNotificationGreeting(Customer customer) {
        if (customer.getLevel().equals("NORMAL")) {
            return "์•ˆ๋…•ํ•˜์„ธ์š”";
        } else if (customer.getLevel().equals("VIP")) {
            return "VIP ๊ณ ๊ฐ๋‹˜";
        } else if (customer.getLevel().equals("PARTNER")) {
            return "ํŒŒํŠธ๋„ˆ๋‹˜";
        } else if (customer.getLevel().equals("STUDENT")) {
            return "ํ•™์ƒ ๊ณ ๊ฐ๋‹˜";
        }
        return "๊ณ ๊ฐ๋‹˜";
    }
    
    // ์ถ”๊ฐ€ ํ˜œํƒ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ โ€” ๋˜๋˜ if ์ง€์˜ฅ
    public boolean canApplyExtraBenefit(Customer customer) {
        if (customer.getLevel().equals("VIP")) return true;
        if (customer.getLevel().equals("PARTNER")) return true;
        return false;
    }
    
    // ๋ฉค๋ฒ„์‹ญ ํฌ์ธํŠธ ์ ๋ฆฝ๋ฅ  โ€” ๋˜๋˜๋˜ if ์ง€์˜ฅ
    public double getPointAccumulationRate(Customer customer) {
        if (customer.getLevel().equals("VIP")) return 0.05;
        if (customer.getLevel().equals("PARTNER")) return 0.10;
        if (customer.getLevel().equals("STUDENT")) return 0.03;
        return 0.01;
    }
    
    // ... ๋“ฑ๊ธ‰ ๊ด€๋ จ ๋ฉ”์„œ๋“œ 30๊ฐœ ๋ชจ๋‘ if ์ง€์˜ฅ โŒ
}

์ƒˆ ๋“ฑ๊ธ‰ (PLATINUM) ์ถ”๊ฐ€ ์‹œ โ€” 30๊ฐœ ๋ฉ”์„œ๋“œ ๋‹ค ์ˆ˜์ • โŒ

public int calculateDiscount(Customer customer, int amount) {
    if (...) return 0;
    else if (...) return amount * 20 / 100;
    else if (...) return amount * 30 / 100;
    else if (...) return amount * 15 / 100;
    else if (customer.getLevel().equals("PLATINUM")) {  // โ† ์ถ”๊ฐ€
        return amount * 25 / 100;
    }
    return 0;
}

public String getNotificationGreeting(Customer customer) {
    // ... ๊ธฐ์กด ๋ถ„๊ธฐ๋“ค ...
    else if (customer.getLevel().equals("PLATINUM")) {  // โ† ๋˜ ์ถ”๊ฐ€
        return "ํ”Œ๋ž˜ํ‹ฐ๋„˜ ๊ณ ๊ฐ๋‹˜";
    }
    return "๊ณ ๊ฐ๋‹˜";
}

// 30๊ฐœ ๋ฉ”์„œ๋“œ์— ๋ชจ๋‘ PLATINUM ๋ถ„๊ธฐ ์ถ”๊ฐ€ โŒ
// ํ•œ ๊ตฐ๋ฐ๋ผ๋„ ๋น ๋œจ๋ฆฌ๋ฉด ๋ฒ„๊ทธ โŒ
// ์ƒˆ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ "PLATINUM ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ์–ด๋”” ์–ด๋”” ์ˆ˜์ •?" ์ถ”์  ๋ถˆ๊ฐ€ โŒ

๋ฌธ์ œ ์ •๋ฆฌ:
1. ์ˆ˜์ • ์˜์—ญ ํญ๋ฐœ โ€” 1๊ฐœ ์ถ”๊ฐ€์— 30๊ฐœ ์ˆ˜์ •
2. ๋ฒ„๊ทธ ์œ„ํ—˜ โ€” ํ•œ ๊ณณ ๋น ๋œจ๋ฆผ
3. ์ถ”์  ๋ถˆ๊ฐ€ โ€” "์ด ๋“ฑ๊ธ‰ ๊ด€๋ จ ๋กœ์ง์ด ์–ด๋”” ์žˆ๋‚˜?"
4. ํ…Œ์ŠคํŠธ ํญ์ฆ โ€” 30๊ฐœ ๋ฉ”์„œ๋“œ ร— 5๊ฐœ ๋“ฑ๊ธ‰ = 150๊ฐœ ์ผ€์ด์Šค
5. OCP ์œ„๋ฐ˜ โ€” "ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ๊ณ , ์ˆ˜์ •์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ" ์˜ ์ •๋ฐ˜๋Œ€


๋‹คํ˜•์„ฑ ํ™œ์šฉ โ€” ๊น”๋”ํ•œ ํ•ด๊ฒฐ โœ…

// ๋ถ€๋ชจ โ€” ์ถ”์ƒ ํด๋ž˜์Šค
public abstract class Customer {
    protected String name;
    protected String email;
    
    // ์ถ”์ƒ ๋ฉ”์„œ๋“œ โ€” ์ž์‹์ด ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„
    public abstract int calculateDiscountRate();
    public abstract String getNotificationGreeting();
    public abstract boolean canApplyExtraBenefit();
    public abstract double getPointAccumulationRate();
    
    // ๊ณตํ†ต ํ–‰๋™
    public int calculateDiscount(int amount) {
        return amount * calculateDiscountRate() / 100;
    }
}

// ๊ฐ ๋“ฑ๊ธ‰ โ€” ์ž๊ธฐ ๋ฐฉ์‹๋Œ€๋กœ ๊ตฌํ˜„
public class NormalCustomer extends Customer {
    @Override public int calculateDiscountRate() { return 0; }
    @Override public String getNotificationGreeting() { return "์•ˆ๋…•ํ•˜์„ธ์š”"; }
    @Override public boolean canApplyExtraBenefit() { return false; }
    @Override public double getPointAccumulationRate() { return 0.01; }
}

public class VipCustomer extends Customer {
    @Override public int calculateDiscountRate() { return 20; }
    @Override public String getNotificationGreeting() { return "VIP ๊ณ ๊ฐ๋‹˜"; }
    @Override public boolean canApplyExtraBenefit() { return true; }
    @Override public double getPointAccumulationRate() { return 0.05; }
}

public class PartnerCustomer extends Customer {
    @Override public int calculateDiscountRate() { return 30; }
    @Override public String getNotificationGreeting() { return "ํŒŒํŠธ๋„ˆ๋‹˜"; }
    @Override public boolean canApplyExtraBenefit() { return true; }
    @Override public double getPointAccumulationRate() { return 0.10; }
}

public class StudentCustomer extends Customer {
    @Override public int calculateDiscountRate() { return 15; }
    @Override public String getNotificationGreeting() { return "ํ•™์ƒ ๊ณ ๊ฐ๋‹˜"; }
    @Override public boolean canApplyExtraBenefit() { return false; }
    @Override public double getPointAccumulationRate() { return 0.03; }
}

Service โ€” if ์ง€์˜ฅ ์‚ฌ๋ผ์ง:

public class FareService {
    
    public int calculateDiscount(Customer customer, int amount) {
        return customer.calculateDiscount(amount);  // โ† ๊ฐ์ฒด์— ์œ„์ž„
    }
    
    public String getNotificationGreeting(Customer customer) {
        return customer.getNotificationGreeting();  // โ† ๊ฐ์ฒด์— ์œ„์ž„
    }
    
    public boolean canApplyExtraBenefit(Customer customer) {
        return customer.canApplyExtraBenefit();  // โ† ๊ฐ์ฒด์— ์œ„์ž„
    }
    
    public double getPointAccumulationRate(Customer customer) {
        return customer.getPointAccumulationRate();  // โ† ๊ฐ์ฒด์— ์œ„์ž„
    }
}

์ƒˆ ๋“ฑ๊ธ‰ (PLATINUM) ์ถ”๊ฐ€ โ€” ์ƒˆ ํด๋ž˜์Šค 1๊ฐœ๋งŒ โœ…

public class PlatinumCustomer extends Customer {
    @Override public int calculateDiscountRate() { return 25; }
    @Override public String getNotificationGreeting() { return "ํ”Œ๋ž˜ํ‹ฐ๋„˜ ๊ณ ๊ฐ๋‹˜"; }
    @Override public boolean canApplyExtraBenefit() { return true; }
    @Override public double getPointAccumulationRate() { return 0.08; }
}

// ๋! โœ…
// ๊ธฐ์กด FareService ์ฝ”๋“œ๋Š” ํ•œ ์ค„๋„ ์•ˆ ๋ฐ”๋€œ
// ์‚ฌ์šฉ ์‹œ:
Customer platinum = new PlatinumCustomer();
fareService.calculateDiscount(platinum, 10000);  // ์ž๋™์œผ๋กœ PlatinumCustomer.calculateDiscount() ํ˜ธ์ถœ

ํšจ๊ณผ ๋น„๊ต

ํ•ญ๋ชฉ๋‹คํ˜•์„ฑ ์—†์ด๋‹คํ˜•์„ฑ ํ™œ์šฉ
์ƒˆ ๋“ฑ๊ธ‰ ์ถ”๊ฐ€ ์‹œ ์ˆ˜์ •30๊ฐœ ๋ฉ”์„œ๋“œ1๊ฐœ ํด๋ž˜์Šค
๋ฒ„๊ทธ ์œ„ํ—˜๋งค์šฐ ๋†’์Œ๋‚ฎ์Œ
์ฝ”๋“œ ์ถ”์ ์–ด๋ ค์›€์‰ฌ์›€ (๊ฐ ๋“ฑ๊ธ‰ ํด๋ž˜์Šค๋งŒ ๋ณด๋ฉด ๋จ)
ํ…Œ์ŠคํŠธ150๊ฐœ ์ผ€์ด์Šค๋“ฑ๊ธ‰๋ณ„ + Service ๋ณ„๋„
OCP ์ค€์ˆ˜โŒโœ…
์ƒˆ ๊ฐœ๋ฐœ์ž ์˜จ๋ณด๋”ฉ์ง€์˜ฅ๋ช…ํ™•

โ†’ ์ด๊ฒŒ ๋‹คํ˜•์„ฑ์˜ ์ง„์งœ ๊ฐ€์น˜.


โœ… 4. ํ•ด๊ฒฐ์ฑ… โ€” ๋‹คํ˜•์„ฑ์˜ ์ž‘๋™ ๋ฐฉ์‹

ํ•ต์‹ฌ ๋ฌธ๋ฒ• โ€” "๋ถ€๋ชจ ํƒ€์ž…์œผ๋กœ ์ž์‹ ๊ฐ์ฒด ๋ฐ›๊ธฐ" โญ

๋ถ€๋ชจํƒ€์ž… ๋ณ€์ˆ˜ = new ์ž์‹ํƒ€์ž…();
๋ณ€์ˆ˜.๋ฉ”์„œ๋“œ();  // ์ž์‹์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋จ

์˜ˆ์‹œ:

Animal animal = new Dog();   // โœ… ๋ถ€๋ชจ ํƒ€์ž… = ์ž์‹ ๊ฐ์ฒด
animal.makeSound();          // "๋ฉ๋ฉ!" โ€” Dog์˜ ๋ฉ”์„œ๋“œ

์™œ ์ด๊ฒŒ ๊ฐ€๋Šฅํ•œ๊ฐ€?:

  • Dog ๋Š” Animal ์˜ ํ•œ ์ข…๋ฅ˜ (is-a ๊ด€๊ณ„)
  • ๋ชจ๋“  Dog ๋Š” Animal ์˜ ์•ฝ์† (๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜) ์„ ์ง€ํ‚ด
  • ๊ทธ๋ž˜์„œ Animal ํƒ€์ž… ๋ณ€์ˆ˜์— ๋‹ด์„ ์ˆ˜ ์žˆ์Œ

๋‹คํ˜•์„ฑ์ด ๊ฐ€๋Šฅํ•œ 3๊ฐ€์ง€ ํ˜•ํƒœ โญ

1. ํด๋ž˜์Šค ์ƒ์†

public abstract class Animal { public abstract void makeSound(); }
public class Dog extends Animal { @Override public void makeSound() { ... } }

Animal a = new Dog();  // โœ…

2. ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

public interface Drawable { void draw(); }
public class Circle implements Drawable { @Override public void draw() { ... } }
public class Square implements Drawable { @Override public void draw() { ... } }

Drawable d = new Circle();  // โœ…
Drawable d2 = new Square();  // โœ…

โ†’ ํ˜„๋Œ€ ์ž๋ฐ”์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ๋Š” ๋ฐฉ์‹.

3. ์ถ”์ƒ ํด๋ž˜์Šค + ๊ตฌํ˜„

public abstract class Shape {
    public abstract double area();
}

public class Circle extends Shape {
    @Override public double area() { ... }
}

Shape s = new Circle();  // โœ…

"๋ฆฌ์ŠคํŠธ๋กœ ๋‹ค์–‘ํ•œ ํƒ€์ž… ๋ฐ›๊ธฐ" โ€” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ํ™œ์šฉ

List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Cow());
animals.add(new Duck());

for (Animal animal : animals) {
    animal.makeSound();  // ๊ฐ์ž ์ž๊ธฐ ๋ฐฉ์‹์œผ๋กœ
}

// ์ถœ๋ ฅ:
// ๋ฉ๋ฉ!
// ์•ผ์˜น~
// ์Œ๋งค
// ๊ฝฅ๊ฝฅ

โ†’ ํƒ€์ž…์„ ์ผ์ผ์ด ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ณ  ํ†ต์ผ๋œ ์ฒ˜๋ฆฌ.


๋ฉ”์„œ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๋‹คํ˜•์„ฑ โญ

public class AnimalShelter {
    public void care(Animal animal) {  // โ† Animal ํƒ€์ž…
        animal.makeSound();
        animal.eat();
        animal.sleep();
    }
}

AnimalShelter shelter = new AnimalShelter();
shelter.care(new Dog());     // โœ…
shelter.care(new Cat());     // โœ…
shelter.care(new Penguin()); // โœ… โ€” ์ƒˆ ๋™๋ฌผ๋„ OK

โ†’ AnimalShelter ์ฝ”๋“œ ํ•œ ์ค„๋„ ์•ˆ ๊ฑด๋“œ๋ฆฌ๊ณ  ์ƒˆ ๋™๋ฌผ ์ฒ˜๋ฆฌ.


๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜์˜ ๋‹คํ˜•์„ฑ

public class AnimalFactory {
    public static Animal create(String type) {
        switch (type) {
            case "dog": return new Dog();
            case "cat": return new Cat();
            default: throw new IllegalArgumentException();
        }
    }
}

Animal a = AnimalFactory.create("dog");
a.makeSound();  // "๋ฉ๋ฉ!"

โ†’ Factory ํŒจํ„ด์˜ ํ† ๋Œ€.


๐Ÿ—๏ธ 5. ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ

ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜ โ€” ๋™์  ๋ฐ”์ธ๋”ฉ (Dynamic Binding) โญโญโญ

๋ฐ”์ธ๋”ฉ(Binding):

"๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๊ณผ ์‹ค์ œ ๋ฉ”์„œ๋“œ ์ฝ”๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ"

๋‘ ์ข…๋ฅ˜:

์ •์  ๋ฐ”์ธ๋”ฉ (Static Binding) โ€” ์ปดํŒŒ์ผ ์‹œ ๊ฒฐ์ •

  • ์–ด๋А ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ• ์ง€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฏธ๋ฆฌ ๊ฒฐ์ •
  • ์˜ˆ: static ๋ฉ”์„œ๋“œ, private ๋ฉ”์„œ๋“œ, final ๋ฉ”์„œ๋“œ, ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋กœ๋”ฉ

๋™์  ๋ฐ”์ธ๋”ฉ (Dynamic Binding) โ€” ๋Ÿฐํƒ€์ž„์— ๊ฒฐ์ • โญ

  • ์–ด๋А ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ• ์ง€ JVM์ด ์‹คํ–‰ ์ค‘์— ๊ฒฐ์ •
  • ์˜ˆ: ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ (์˜ค๋ฒ„๋ผ์ด๋“œ๋œ ๊ฒƒ)

๋™์  ๋ฐ”์ธ๋”ฉ์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜ โ€” VMT (Virtual Method Table) โญโญ

JVM์€ ๊ฐ ํด๋ž˜์Šค๋งˆ๋‹ค VMT (Virtual Method Table) ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

public class Animal {
    public void makeSound() { System.out.println("๋™๋ฌผ ์†Œ๋ฆฌ"); }
    public void eat() { System.out.println("๋จน๋‹ค"); }
}

public class Dog extends Animal {
    @Override
    public void makeSound() { System.out.println("๋ฉ๋ฉ!"); }
    public void wagTail() { System.out.println("๊ผฌ๋ฆฌ ํ”๋“ค๊ธฐ"); }
}

JVM์ด ๋งŒ๋“œ๋Š” VMT:

Animal์˜ VMT:
[0] makeSound โ†’ Animal.makeSound
[1] eat       โ†’ Animal.eat
[2] toString  โ†’ Object.toString
[3] equals    โ†’ Object.equals
...

Dog์˜ VMT (Animal ์ƒ์† + ์˜ค๋ฒ„๋ผ์ด๋“œ + ์ถ”๊ฐ€):
[0] makeSound โ†’ Dog.makeSound  โ† ์˜ค๋ฒ„๋ผ์ด๋“œ๋จ โญ
[1] eat       โ†’ Animal.eat     โ† ์ƒ์†๋ฐ›์Œ
[2] toString  โ†’ Object.toString
[3] equals    โ†’ Object.equals
[4] wagTail   โ†’ Dog.wagTail    โ† Dog๋งŒ์˜ ์ƒˆ ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์˜ ์‹ค์ œ ํ๋ฆ„ โญโญ

Animal animal = new Dog();
animal.makeSound();

JVM ๋‚ด๋ถ€ ๋™์ž‘:

1. animal.makeSound() ํ˜ธ์ถœ
        โ†“
2. JVM: "animal ๋ณ€์ˆ˜์˜ ์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…์€? Animal"
        โ†“
3. JVM: "animal์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ค์ œ ๊ฐ์ฒด๋Š”? Dog ์ธ์Šคํ„ด์Šค"
        โ†“
4. JVM: "Dog ์ธ์Šคํ„ด์Šค์˜ ํด๋ž˜์Šค ์ •๋ณด (Class) ํ™•์ธ"
        โ†“
5. JVM: "Dog์˜ VMT์—์„œ makeSound ์ฐพ๊ธฐ"
        โ†“
6. VMT[0] = Dog.makeSound  โ† ๋ฐœ๊ฒฌ
        โ†“
7. Dog.makeSound() ์‹คํ–‰ โ†’ "๋ฉ๋ฉ!"

ํ•ต์‹ฌ:

"์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…์€ 'Animal' ์ด์ง€๋งŒ, ๋Ÿฐํƒ€์ž„์— ์‹ค์ œ ๊ฐ์ฒด (Dog) ์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋จ"

โ†’ ์ด๊ฒŒ ๋™์  ๋ฐ”์ธ๋”ฉ.


์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž… vs ๋Ÿฐํƒ€์ž„ ํƒ€์ž… โญ (์ž๊ธฐ ์ ๊ฒ€ Q2)

Animal animal = new Dog();
//  โ†‘           โ†‘
// ์ปดํŒŒ์ผ ํƒ€์ž„  ๋Ÿฐํƒ€์ž„ ํƒ€์ž…
//  ํƒ€์ž… (์ฐธ์กฐ)  (์‹ค์ œ ๊ฐ์ฒด)

์–ด๋–ค ์ฐจ์ด๋ฅผ ๋งŒ๋“œ๋‚˜?

1. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ (์ปดํŒŒ์ผ ์‹œ ๊ฒฐ์ •)

Animal animal = new Dog();

animal.makeSound();  // โœ… Animal์— ์žˆ์Œ
animal.eat();        // โœ… Animal์— ์žˆ์Œ
animal.wagTail();    // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” Animal์—๋Š” wagTail ์—†์Œ

โ†’ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ณ€์ˆ˜์˜ ์„ ์–ธ ํƒ€์ž…(Animal) ๋งŒ ๋ด„.

2. ์‹ค์ œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ (๋Ÿฐํƒ€์ž„ ๊ฒฐ์ •)

Animal animal = new Dog();
animal.makeSound();  // "๋ฉ๋ฉ!" โ€” Dog์˜ ๋ฉ”์„œ๋“œ

โ†’ JVM์€ ์‹ค์ œ ๊ฐ์ฒด (Dog) ์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ.


์ •์  ๋ฐ”์ธ๋”ฉ vs ๋™์  ๋ฐ”์ธ๋”ฉ ๋น„๊ต โญ

public class Parent {
    public static void staticMethod() { System.out.println("Parent static"); }
    public void instanceMethod() { System.out.println("Parent instance"); }
}

public class Child extends Parent {
    public static void staticMethod() { System.out.println("Child static"); }
    @Override
    public void instanceMethod() { System.out.println("Child instance"); }
}

Parent p = new Child();

p.staticMethod();    // โ“
p.instanceMethod();  // โ“

๊ฒฐ๊ณผ:

Parent static    โ† ์ •์  ๋ฐ”์ธ๋”ฉ โ€” ๋ณ€์ˆ˜ ํƒ€์ž…(Parent)์œผ๋กœ ๊ฒฐ์ •
Child instance   โ† ๋™์  ๋ฐ”์ธ๋”ฉ โ€” ์‹ค์ œ ๊ฐ์ฒด(Child)๋กœ ๊ฒฐ์ •

ํ•ต์‹ฌ ์ฐจ์ด โญ :

  • static ๋ฉ”์„œ๋“œ: ์˜ค๋ฒ„๋ผ์ด๋“œ X, ๋‹จ์ˆœํžˆ ๊ฐ€๋ ค์ง (hiding)
  • ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ: ์˜ค๋ฒ„๋ผ์ด๋“œ O, ๋™์  ๋ฐ”์ธ๋”ฉ

ํ•„๋“œ๋„ ์ •์  ๋ฐ”์ธ๋”ฉ โš ๏ธ

public class Parent {
    public String name = "Parent";
}

public class Child extends Parent {
    public String name = "Child";
}

Parent p = new Child();
System.out.println(p.name);  // โ“

๊ฒฐ๊ณผ: "Parent" โ€” ํ•„๋“œ๋Š” ๋ณ€์ˆ˜ ํƒ€์ž…์œผ๋กœ ๊ฒฐ์ •.

โ†’ ํ•„๋“œ๋Š” ๋‹คํ˜•์„ฑ X, ๋ฉ”์„œ๋“œ๋งŒ ๋‹คํ˜•์„ฑ O.

ํ•ด๊ฒฐ: getter ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ:

public class Parent {
    private String name = "Parent";
    public String getName() { return name; }  // ๋ฉ”์„œ๋“œ๋Š” ๋‹คํ˜•์„ฑ O
}

public class Child extends Parent {
    private String name = "Child";
    @Override
    public String getName() { return name; }
}

Parent p = new Child();
System.out.println(p.getName());  // "Child" โœ…

โ†’ ํ•„๋“œ๋Š” ์ง์ ‘ ๋…ธ์ถœํ•˜์ง€ ๋ง๊ณ  ๋ฉ”์„œ๋“œ๋กœ โ€” ์บก์Аํ™” + ๋‹คํ˜•์„ฑ.


๋‹คํ˜•์„ฑ๊ณผ ์ƒ์„ฑ์ž โš ๏ธ (Effective Java ํ•จ์ •)

public class Parent {
    public Parent() {
        init();  // โš ๏ธ ์œ„ํ—˜!
    }
    public void init() { System.out.println("Parent init"); }
}

public class Child extends Parent {
    private String name = "Child";
    
    @Override
    public void init() {
        System.out.println("Child init: " + name.length());  // NPE!
    }
}

new Child();

์™œ NPE?:
1. new Child() โ†’ Parent ์ƒ์„ฑ์ž ๋จผ์ € ์‹คํ–‰
2. Parent ์ƒ์„ฑ์ž์—์„œ init() ํ˜ธ์ถœ
3. ๋‹คํ˜•์„ฑ ์œผ๋กœ Child์˜ init() ์‹คํ–‰
4. ๊ทธ๋Ÿฌ๋‚˜ ์ด ์‹œ์ ์— Child์˜ name ์€ ์•„์ง ์ดˆ๊ธฐํ™” X (null)
5. name.length() โ†’ NPE

๊ตํ›ˆ: ์ƒ์„ฑ์ž์—์„œ ์˜ค๋ฒ„๋ผ์ด๋“œ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ X.


๐Ÿ’ป 6. ์‹ค์ „ ์ฝ”๋“œ ์˜ˆ์‹œ

ILIC ์šด์ž„ ์‹œ์Šคํ…œ์—์„œ ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•˜๋Š” ๋‹ค์–‘ํ•œ ํŒจํ„ด.

์˜ˆ์‹œ 1: ์•Œ๋ฆผ ์ฑ„๋„์˜ ๋‹คํ˜•์„ฑ

// ์ธํ„ฐํŽ˜์ด์Šค โ€” ์•Œ๋ฆผ์˜ ์•ฝ์†
public interface NotificationSender {
    void send(String message, String recipient);
}

// ๋‹ค์–‘ํ•œ ๊ตฌํ˜„์ฒด
public class EmailSender implements NotificationSender {
    @Override
    public void send(String message, String recipient) {
        System.out.println("[Email] " + recipient + ": " + message);
        // ์‹ค์ œ ์ด๋ฉ”์ผ ๋ฐœ์†ก ๋กœ์ง
    }
}

public class SmsSender implements NotificationSender {
    @Override
    public void send(String message, String recipient) {
        System.out.println("[SMS] " + recipient + ": " + message);
    }
}

public class SlackSender implements NotificationSender {
    @Override
    public void send(String message, String recipient) {
        System.out.println("[Slack] " + recipient + ": " + message);
    }
}

public class KakaoTalkSender implements NotificationSender {
    @Override
    public void send(String message, String recipient) {
        System.out.println("[KakaoTalk] " + recipient + ": " + message);
    }
}

// ์‚ฌ์šฉ โ€” ๋‹คํ˜•์„ฑ ํ™œ์šฉ
public class NotificationService {
    private final List<NotificationSender> senders;
    
    public NotificationService(List<NotificationSender> senders) {
        this.senders = senders;
    }
    
    public void notifyAll(String message, String recipient) {
        for (NotificationSender sender : senders) {
            sender.send(message, recipient);  // โ† ๋‹คํ˜•์„ฑ!
        }
    }
}

// ํ˜ธ์ถœ
List<NotificationSender> senders = List.of(
    new EmailSender(),
    new SmsSender(),
    new SlackSender(),
    new KakaoTalkSender()
);

NotificationService service = new NotificationService(senders);
service.notifyAll("์šด์ž„์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค", "alice@example.com");

ํšจ๊ณผ:

  • ์ƒˆ ์ฑ„๋„ (Telegram) ์ถ”๊ฐ€ ์‹œ โ†’ ์ƒˆ ํด๋ž˜์Šค 1๊ฐœ๋งŒ, NotificationService ์•ˆ ๊ฑด๋“œ๋ฆผ
  • ์ฑ„๋„๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐœ์†ก ๋กœ์ง, ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ†ต์ผ
  • โ†’ Spring DI ํŒจํ„ด์˜ ํ† ๋Œ€

์˜ˆ์‹œ 2: ์šด์ž„ ์ข…๋ฅ˜๋ณ„ ์ฒ˜๋ฆฌ

// ๋ถ€๋ชจ ์ถ”์ƒ ํด๋ž˜์Šค
public abstract class Fare {
    protected Long id;
    protected int amount;
    protected FareStatus status;
    
    public Fare(Long id, int amount) {
        this.id = id;
        this.amount = amount;
        this.status = FareStatus.DRAFT;
    }
    
    // ์ถ”์ƒ ๋ฉ”์„œ๋“œ โ€” ์ข…๋ฅ˜๋ณ„๋กœ ๋‹ค๋ฆ„
    public abstract int calculateTotal();
    public abstract String getDescription();
    
    // ๊ณตํ†ต ๋ฉ”์„œ๋“œ โ€” ๋ชจ๋“  ์šด์ž„์— ๊ณตํ†ต
    public void submit() {
        if (status != FareStatus.DRAFT) {
            throw new IllegalStateException();
        }
        status = FareStatus.SUBMITTED;
    }
}

public class StandardFare extends Fare {
    public StandardFare(Long id, int amount) {
        super(id, amount);
    }
    
    @Override
    public int calculateTotal() {
        return amount;
    }
    
    @Override
    public String getDescription() {
        return "์ผ๋ฐ˜ ์šด์ž„";
    }
}

public class UrgentFare extends Fare {
    private int urgentFee;
    
    public UrgentFare(Long id, int amount, int urgentFee) {
        super(id, amount);
        this.urgentFee = urgentFee;
    }
    
    @Override
    public int calculateTotal() {
        return amount + urgentFee;
    }
    
    @Override
    public String getDescription() {
        return "๊ธด๊ธ‰ ์šด์ž„ (๊ธด๊ธ‰๋น„: " + urgentFee + ")";
    }
}

public class InternationalFare extends Fare {
    private String currency;
    private double exchangeRate;
    
    public InternationalFare(Long id, int amount, String currency, double rate) {
        super(id, amount);
        this.currency = currency;
        this.exchangeRate = rate;
    }
    
    @Override
    public int calculateTotal() {
        return (int)(amount * exchangeRate);
    }
    
    @Override
    public String getDescription() {
        return "๊ตญ์ œ ์šด์ž„ (" + currency + " " + exchangeRate + ")";
    }
}

// ์‚ฌ์šฉ โ€” ๋‹คํ˜•์„ฑ์œผ๋กœ ํ†ต์ผ๋œ ์ฒ˜๋ฆฌ
List<Fare> fares = List.of(
    new StandardFare(1L, 50000),
    new UrgentFare(2L, 50000, 10000),
    new InternationalFare(3L, 100, "USD", 1300.0)
);

int totalRevenue = 0;
for (Fare fare : fares) {
    totalRevenue += fare.calculateTotal();  // โ† ๋‹คํ˜•์„ฑ!
    System.out.println(fare.getDescription());
}

์ถœ๋ ฅ:

์ผ๋ฐ˜ ์šด์ž„
๊ธด๊ธ‰ ์šด์ž„ (๊ธด๊ธ‰๋น„: 10000)
๊ตญ์ œ ์šด์ž„ (USD 1300.0)

โ†’ ์ƒˆ ์šด์ž„ ์ข…๋ฅ˜ ์ถ”๊ฐ€ ์‹œ ์ด ์ฝ”๋“œ๋Š” ํ•œ ์ค„๋„ ์•ˆ ๋ฐ”๋€œ.


์˜ˆ์‹œ 3: ๊ฒฐ์ œ ์ˆ˜๋‹จ์˜ ๋‹คํ˜•์„ฑ

public interface PaymentMethod {
    void process(int amount);
    String getMethodName();
}

public class CreditCardPayment implements PaymentMethod {
    private String cardNumber;
    
    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }
    
    @Override
    public void process(int amount) {
        System.out.println("์‹ ์šฉ์นด๋“œ " + cardNumber + " ๋กœ " + amount + "์› ๊ฒฐ์ œ");
    }
    
    @Override
    public String getMethodName() {
        return "์‹ ์šฉ์นด๋“œ";
    }
}

public class BankTransferPayment implements PaymentMethod {
    private String accountNumber;
    
    public BankTransferPayment(String accountNumber) {
        this.accountNumber = accountNumber;
    }
    
    @Override
    public void process(int amount) {
        System.out.println("๊ณ„์ขŒ " + accountNumber + " ์—์„œ " + amount + "์› ์ด์ฒด");
    }
    
    @Override
    public String getMethodName() {
        return "๊ณ„์ขŒ์ด์ฒด";
    }
}

public class KakaoPayPayment implements PaymentMethod {
    @Override
    public void process(int amount) {
        System.out.println("์นด์นด์˜คํŽ˜์ด๋กœ " + amount + "์› ๊ฒฐ์ œ");
    }
    
    @Override
    public String getMethodName() {
        return "์นด์นด์˜คํŽ˜์ด";
    }
}

// ์‚ฌ์šฉ
public class CheckoutService {
    public void checkout(Fare fare, PaymentMethod paymentMethod) {
        int total = fare.calculateTotal();
        paymentMethod.process(total);
        System.out.println(paymentMethod.getMethodName() + " ๊ฒฐ์ œ ์™„๋ฃŒ");
    }
}

CheckoutService service = new CheckoutService();
Fare fare = new StandardFare(1L, 50000);

service.checkout(fare, new CreditCardPayment("1234-5678"));
service.checkout(fare, new BankTransferPayment("110-1234"));
service.checkout(fare, new KakaoPayPayment());

ํšจ๊ณผ:

  • ์ƒˆ ๊ฒฐ์ œ ์ˆ˜๋‹จ (PayPal, Apple Pay) ์ถ”๊ฐ€ ์‹œ โ†’ ์ƒˆ ํด๋ž˜์Šค๋งŒ
  • CheckoutService ์ฝ”๋“œ๋Š” ์•ˆ ๋ฐ”๋€œ
  • โ†’ Strategy ํŒจํ„ด์˜ ์ •ํ™•ํ•œ ์‚ฌ๋ก€

์˜ˆ์‹œ 4: ์ปฌ๋ ‰์…˜ ๋‹คํ˜•์„ฑ

// List ์ธํ„ฐํŽ˜์ด์Šค โ€” ๋‹ค์–‘ํ•œ ๊ตฌํ˜„์ฒด
List<String> arrayList = new ArrayList<>();    // ๋น ๋ฅธ ์กฐํšŒ
List<String> linkedList = new LinkedList<>();   // ๋น ๋ฅธ ์‚ฝ์ž…/์‚ญ์ œ
List<String> immutableList = List.of("a", "b"); // ๋ถˆ๋ณ€

// ๋™์ผํ•œ List ํƒ€์ž…์œผ๋กœ ํ†ต์ผ๋œ ์ฒ˜๋ฆฌ
public void process(List<String> list) {
    for (String item : list) {
        System.out.println(item);
    }
}

process(arrayList);    // OK
process(linkedList);   // OK
process(immutableList); // OK

์ž๋ฐ” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž์ฒด๊ฐ€ ๋‹คํ˜•์„ฑ์˜ ๊ฑฐ๋Œ€ํ•œ ์˜ˆ์‹œ:

  • Collection โ† List, Set, Queue
  • List โ† ArrayList, LinkedList, Vector
  • Map โ† HashMap, TreeMap, LinkedHashMap

โ†’ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์•ฝ์†, ๊ตฌํ˜„์ฒด๋Š” ์ž์œ .


์˜ˆ์‹œ 5: Spring DI์—์„œ์˜ ๋‹คํ˜•์„ฑ โญ

๋ฏธ๋ž˜ ํ•™์Šต ์ฃผ์ฐจ์ด์ง€๋งŒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ:

// ์ธํ„ฐํŽ˜์ด์Šค
public interface UserRepository {
    User findById(Long id);
}

// ๋‹ค์–‘ํ•œ ๊ตฌํ˜„
@Repository
public class JpaUserRepository implements UserRepository { ... }

// ๋˜๋Š”
@Repository
public class MongoUserRepository implements UserRepository { ... }

// ์‚ฌ์šฉ
@Service
public class UserService {
    private final UserRepository repository;  // ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…
    
    public UserService(UserRepository repository) {
        this.repository = repository;
    }
    
    public User getUser(Long id) {
        return repository.findById(id);  // ์–ด๋А ๊ตฌํ˜„์ด๋“  OK
    }
}

Spring์ด ์ž๋™์œผ๋กœ:

  • UserRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋นˆ์„ ์ฐพ์Œ
  • UserService ์— ์ž๋™ ์ฃผ์ž…
  • โ†’ ๋‚˜์ค‘์— MongoDB๋กœ ๋ฐ”๊พธ๋ ค๋ฉด ๊ตฌํ˜„์ฒด๋งŒ ๊ต์ฒด

โ†’ Spring ์ „์ฒด๊ฐ€ ๋‹คํ˜•์„ฑ ์œ„์— ์„ค๊ณ„๋จ. 5์ฃผ์ฐจ์—์„œ ๋ณธ๊ฒฉ ํ•™์Šต.


โš ๏ธ 7. ์ฃผ์˜์‚ฌํ•ญ & ํ”ํ•œ ์‹ค์ˆ˜

์‹ค์ˆ˜ 1: @Override ๋ˆ„๋ฝ โš ๏ธ

public class Animal {
    public void makeSound() { ... }
}

public class Dog extends Animal {
    public void makesound() {  // ์˜คํƒ€: 's' ๊ฐ€ ์†Œ๋ฌธ์ž
        System.out.println("๋ฉ๋ฉ");
    }
}

Animal a = new Dog();
a.makeSound();  // "๋™๋ฌผ ์†Œ๋ฆฌ" โ€” ์˜ค๋ฒ„๋ผ์ด๋“œ ์•ˆ ๋จ! โŒ

ํ•ด๊ฒฐ: ๋ชจ๋“  ์˜ค๋ฒ„๋ผ์ด๋“œ์— @Override ํ•„์ˆ˜.

@Override
public void makesound() {  // ์ปดํŒŒ์ผ ์—๋Ÿฌ ์ฆ‰์‹œ ๋ฐœ๊ฒฌ
    ...
}

์‹ค์ˆ˜ 2: ํ•„๋“œ๋Š” ๋‹คํ˜•์„ฑ X ๋ผ๋Š” ์‚ฌ์‹ค ๋ชจ๋ฆ„

public class Parent {
    public String name = "Parent";
}

public class Child extends Parent {
    public String name = "Child";
}

Parent p = new Child();
System.out.println(p.name);  // "Parent" โ€” ํ•„๋“œ๋Š” ๋ณ€์ˆ˜ ํƒ€์ž…์œผ๋กœ!

โ†’ ํ•„๋“œ๋Š” ๋‹คํ˜•์„ฑ X. ๋ฉ”์„œ๋“œ๋งŒ ๋‹คํ˜•์„ฑ.

ํ•ด๊ฒฐ: ํ•„๋“œ๋ฅผ private์œผ๋กœ + getter ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ.


์‹ค์ˆ˜ 3: static ๋ฉ”์„œ๋“œ๋Š” ์˜ค๋ฒ„๋ผ์ด๋“œ X

public class Parent {
    public static void method() { System.out.println("Parent"); }
}

public class Child extends Parent {
    public static void method() { System.out.println("Child"); }
}

Parent p = new Child();
p.method();  // "Parent" โ€” static์€ ์ปดํŒŒ์ผ ์‹œ ๊ฒฐ์ •!

์™œ?: static ๋ฉ”์„œ๋“œ๋Š” ์ธ์Šคํ„ด์Šค๊ฐ€ ์•„๋‹Œ ํด๋ž˜์Šค์— ์†ํ•จ. ๋‹คํ˜•์„ฑ ์ ์šฉ X.

โ†’ static ๋ฉ”์„œ๋“œ๋Š” ์ˆจ๊น€(hiding) ์ผ ๋ฟ ์˜ค๋ฒ„๋ผ์ด๋“œ X.

ํŒ: static ๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค๋ช…์œผ๋กœ ํ˜ธ์ถœ ๊ถŒ์žฅ:

Parent.method();  // ๋ช…์‹œ์ 
Child.method();

์‹ค์ˆ˜ 4: instanceof ๋‚จ๋ฐœ

// โŒ ๋‹คํ˜•์„ฑ์„ ์™ธ๋ฉดํ•œ ์ฝ”๋“œ
public void process(Animal animal) {
    if (animal instanceof Dog) {
        ((Dog) animal).bark();
    } else if (animal instanceof Cat) {
        ((Cat) animal).meow();
    }
}

โ†’ ๋‹คํ˜•์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ. if ์ง€์˜ฅ์˜ ๋ถ€ํ™œ.

ํ•ด๊ฒฐ: ๊ณตํ†ต ๋ฉ”์„œ๋“œ๋ฅผ ๋ถ€๋ชจ์— ๋‘๊ธฐ:

public abstract class Animal {
    public abstract void makeSound();
}

public void process(Animal animal) {
    animal.makeSound();  // โœ… ๋‹คํ˜•์„ฑ!
}

โ†’ instanceof ๊ฐ€ ์ž์ฃผ ๋“ฑ์žฅํ•˜๋ฉด ์„ค๊ณ„ ์žฌ๊ฒ€ํ†  ์‹ ํ˜ธ.


์‹ค์ˆ˜ 5: ์บ์ŠคํŒ… ํ›„ NullPointerException

Animal animal = new Cat();
Dog dog = (Dog) animal;  // โš ๏ธ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ: ClassCastException
dog.bark();

ํ•ด๊ฒฐ: ์บ์ŠคํŒ… ์ „ instanceof ์ฒดํฌ (Unit 2.5์—์„œ ์ž์„ธํžˆ):

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.bark();
}

๋˜๋Š” Java 16+ ํŒจํ„ด ๋งค์นญ:

if (animal instanceof Dog dog) {
    dog.bark();
}

์‹ค์ˆ˜ 6: ์ถ”์ƒ ๋ฉ”์„œ๋“œ ๋ˆ„๋ฝ

public abstract class Animal {
    public abstract void makeSound();
}

public class Dog extends Animal {
    // makeSound() ๊ตฌํ˜„ ์•ˆ ํ•จ
}
// โ†’ ์ปดํŒŒ์ผ ์—๋Ÿฌ

๊ทœ์น™: ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋Š” ์ž์‹์ด ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ (์ž์‹๋„ abstract๋ฉด OK).


์‹ค์ˆ˜ 7: ๋ถ€๋ชจ-์ž์‹ ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋‹ค๋ฆ„

public class Animal {
    public void eat(String food) { ... }
}

public class Dog extends Animal {
    @Override
    public void eat(int amount) { ... }  // โŒ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋‹ค๋ฆ„ โ†’ ์˜ค๋ฒ„๋กœ๋”ฉ ๋จ
}

โ†’ @Override ๊ฐ€ ์žˆ์–ด์„œ ์ปดํŒŒ์ผ ์—๋Ÿฌ๋กœ ์žกํž˜. ์—†์œผ๋ฉด ์˜ค๋ฒ„๋กœ๋”ฉ (๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ) ์œผ๋กœ ์ธ์‹ โ†’ ๋‹คํ˜•์„ฑ X.


์‹ค์ˆ˜ 8: ๋‹คํ˜•์„ฑ๊ณผ equals/hashCode

public class Customer {
    private Long id;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;  // โš ๏ธ
        return Objects.equals(id, ((Customer) obj).id);
    }
}

public class VipCustomer extends Customer { ... }

Customer c1 = new Customer(1L);
Customer c2 = new VipCustomer(1L);

c1.equals(c2);  // false โ€” getClass() ๋‹ค๋ฆ„

๋ฌธ์ œ: ๋ถ€๋ชจ-์ž์‹ ๋น„๊ต ๋ถˆ๊ฐ€.

ํ•ด๊ฒฐ ์‹œ ์ฃผ์˜: instanceof ์‚ฌ์šฉ vs getClass() โ€” Joshua Bloch๋Š” ์ผ๊ด€์„ฑ ์œ„ํ•ด getClass() ๊ถŒ์žฅ. ๊นŠ์€ ์ฃผ์ œ์ด์ง€๋งŒ, ์ƒ์† ๊ตฌ์กฐ + equals ๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ.


๐Ÿ”— 8. ์—ฐ๊ด€ ๊ฐœ๋… ๋งต

์ง์ ‘ ์ด์–ด์ง€๋Š” ํ•™์Šต

[Unit 2.3: ์ƒ์†๊ณผ ์ƒ์„ฑ์ž ์ฒด์ด๋‹]
        โ†“
[Unit 2.4: ๋‹คํ˜•์„ฑ] โ˜…โ˜…โ˜…  โ† ์ง€๊ธˆ ์—ฌ๊ธฐ
        โ†“
[Unit 2.5: instanceof์™€ ํ˜•๋ณ€ํ™˜] โ€” ๋‹คํ˜•์„ฑ์˜ ์•ˆ์ „์žฅ์น˜
        โ†“
[Unit 2.6: Nested/Inner/Anonymous]

์ด Unit์˜ ๊ฐœ๋…์ด ํ™œ์šฉ๋˜๋Š” ๊ณณ

1์ฃผ์ฐจ ๋‚ด:

  • Unit 2.5: instanceof โ€” ๋‹คํ˜•์„ฑ์—์„œ ์‹ค์ œ ํƒ€์ž… ํ™•์ธ
  • Phase 3 (SOLID): OCP, LSP, DIP ๋ชจ๋‘ ๋‹คํ˜•์„ฑ ๊ธฐ๋ฐ˜
  • Phase 6 (์ปฌ๋ ‰์…˜): List/Map ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋‹คํ˜•์„ฑ ํ™œ์šฉ

๋ฏธ๋ž˜ ์ฃผ์ฐจ:

  • 3์ฃผ์ฐจ (์ œ๋„ค๋ฆญ): ๋‹คํ˜•์„ฑ + ํƒ€์ž… ์•ˆ์ „์„ฑ ๊ฒฐํ•ฉ
  • 3์ฃผ์ฐจ (๋žŒ๋‹ค): ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋‹คํ˜•์„ฑ
  • 5์ฃผ์ฐจ (Spring DI): ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ์˜์กด์„ฑ ์ฃผ์ž…์˜ ํ† ๋Œ€
  • 5์ฃผ์ฐจ (๋””์ž์ธ ํŒจํ„ด): Strategy, Template Method, Factory ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ํŒจํ„ด
  • 8-9์ฃผ์ฐจ (AOP): ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์›๋ณธ์„ ๋‹คํ˜•์ ์œผ๋กœ ๋Œ€์ฒด
  • 11-12์ฃผ์ฐจ (JPA): Entity ์ƒ์† (@Inheritance)
  • 17์ฃผ์ฐจ (MSA): ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์„œ๋น„์Šค ์•ฝ์†

SOLID์™€ ๋‹คํ˜•์„ฑ โญ

์›์น™๋‹คํ˜•์„ฑ๊ณผ์˜ ๊ด€๊ณ„
SRP์ฑ…์ž„ ๋ถ„๋ฆฌ ํ›„ ๋‹คํ˜•์„ฑ์œผ๋กœ ์กฐ๋ฆฝ
OCP๋‹คํ˜•์„ฑ์˜ ์ง์ ‘์  ๊ฒฐ๊ณผ โ€” ํ™•์žฅ ์—ด๋ ค์žˆ๊ณ  ์ˆ˜์ • ๋‹ซํ˜€์žˆ์Œ
LSP๋‹คํ˜•์„ฑ์˜ ์ •ํ™•ํ•œ ๊ทœ์น™ โ€” ์ž์‹์ด ๋ถ€๋ชจ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋Œ€์ฒด
ISP์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ โ†’ ๋‹คํ˜•์„ฑ ๋‹จ์œ„ ์ž‘๊ฒŒ
DIP์ถ”์ƒํ™”์— ์˜์กด โ†’ ๋‹คํ˜•์„ฑ ์—†์ด ๋ถˆ๊ฐ€๋Šฅ

โ†’ SOLID 5์›์น™ ์ค‘ 4๊ฐœ๊ฐ€ ๋‹คํ˜•์„ฑ์— ์ง์ ‘ ์˜์กด. Phase 3์—์„œ ์ž์„ธํžˆ.


๋””์ž์ธ ํŒจํ„ด๊ณผ ๋‹คํ˜•์„ฑ

๊ฑฐ์˜ ๋ชจ๋“  GoF ํŒจํ„ด์ด ๋‹คํ˜•์„ฑ ๊ธฐ๋ฐ˜:

ํŒจํ„ด๋‹คํ˜•์„ฑ ํ™œ์šฉ
Strategy์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ, ๊ตฌํ˜„์ฒด ๊ต์ฒด
Template Method๋ถ€๋ชจ๊ฐ€ ํ๋ฆ„, ์ž์‹์ด ๊ตฌ์ฒด
Factory๊ฐ์ฒด ์ƒ์„ฑ์„ ์ถ”์ƒํ™”
Decorator๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
Observer๋‹ค์–‘ํ•œ Observer๋ฅผ ํ†ต์ผ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ
Command๋ช…๋ น์„ ๊ฐ์ฒด๋กœ ์ถ”์ƒํ™”
State์ƒํƒœ๋ณ„ ๋‹ค๋ฅธ ๋™์ž‘์„ ๋‹คํ˜•์„ฑ์œผ๋กœ

โ†’ ๋‹คํ˜•์„ฑ์„ ๋ชจ๋ฅด๋ฉด ๋””์ž์ธ ํŒจํ„ด ํ•™์Šต ๋ถˆ๊ฐ€.


๋ฉด์ ‘ ๋‹จ๊ณจ ์งˆ๋ฌธ ๋งคํ•‘

์งˆ๋ฌธ์ด Unit์—์„œ์˜ ๋‹ต
"๋‹คํ˜•์„ฑ์ด๋ž€?"๊ฐ™์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ๊ฐ์ฒด์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘
"๋‹คํ˜•์„ฑ์˜ ์žฅ์ ?"์ƒˆ ์ข…๋ฅ˜ ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ์•ˆ ๊ฑด๋“œ๋ฆผ (OCP)
"๋™์  ๋ฐ”์ธ๋”ฉ์ด๋ž€?"๋Ÿฐํƒ€์ž„์— ์‹ค์ œ ๊ฐ์ฒด ํƒ€์ž…์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ
"์ •์  ๋ฐ”์ธ๋”ฉ vs ๋™์  ๋ฐ”์ธ๋”ฉ?"static/private/final = ์ •์ , ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ = ๋™์ 
"VMT๊ฐ€ ๋ญ”๊ฐ€์š”?"ํด๋ž˜์Šค๋ณ„ ๋ฉ”์„œ๋“œ ํ…Œ์ด๋ธ”, JVM์ด ๋™์  ๋ฐ”์ธ๋”ฉ์— ์‚ฌ์šฉ
"ํ•„๋“œ๋„ ๋‹คํ˜•์„ฑ?"NO โ€” ํ•„๋“œ๋Š” ๋ณ€์ˆ˜ ํƒ€์ž…์œผ๋กœ ๊ฒฐ์ •
"์˜ค๋ฒ„๋กœ๋”ฉ vs ์˜ค๋ฒ„๋ผ์ด๋”ฉ?"์˜ค๋ฒ„๋กœ๋”ฉ=๊ฐ™์€ ํด๋ž˜์Šค ๋‹ค๋ฅธ ์‹œ๊ทธ๋‹ˆ์ฒ˜, ์˜ค๋ฒ„๋ผ์ด๋”ฉ=์ƒ์†์—์„œ ์žฌ์ •์˜

๐Ÿ“ 9. ํ•ต์‹ฌ ์š”์•ฝ โ€” 3์ค„ ์ •๋ฆฌ

1๏ธโƒฃ ๋‹คํ˜•์„ฑ์€ "๊ฐ™์€ ๋ฉ”์‹œ์ง€์— ๊ฐ์ฒด๋งˆ๋‹ค ๋‹ค๋ฅธ ์‘๋‹ต" ์ด๋‹ค.

๋ถ€๋ชจ ํƒ€์ž… ๋ณ€์ˆ˜๊ฐ€ ์ž์‹ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๊ณ  (Animal a = new Dog()), ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ JVM์ด ๋Ÿฐํƒ€์ž„์— ์‹ค์ œ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์•„ ์‹คํ–‰ (๋™์  ๋ฐ”์ธ๋”ฉ). ์ด๊ฒŒ OOP 4๋Œ€ ์›์น™์˜ ์ •์ ์ด๋ฉฐ, ์ž๋ฐ” ์ƒํƒœ๊ณ„ (Spring, JPA, ๋””์ž์ธ ํŒจํ„ด) ์˜ ํ† ๋Œ€.

2๏ธโƒฃ VMT (Virtual Method Table) ๊ฐ€ ๋‹คํ˜•์„ฑ์˜ ๋น„๋ฐ€์ด๋‹ค.

๊ฐ ํด๋ž˜์Šค๋Š” ์ž๊ธฐ๋งŒ์˜ VMT๋ฅผ ๊ฐ€์ง€๊ณ , ์˜ค๋ฒ„๋ผ์ด๋“œ๋œ ๋ฉ”์„œ๋“œ๋Š” ์ž์‹ ํด๋ž˜์Šค์˜ VMT์— ๋“ฑ๋ก๋จ. JVM์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ ์ฐธ์กฐ ํƒ€์ž…์ด ์•„๋‹Œ ์‹ค์ œ ๊ฐ์ฒด์˜ VMT ๋ฅผ ์ฐธ์กฐ. ๋‹จ, static/private/final ๋ฉ”์„œ๋“œ์™€ ํ•„๋“œ๋Š” ์ •์  ๋ฐ”์ธ๋”ฉ โ€” ๋‹คํ˜•์„ฑ X.

3๏ธโƒฃ ๋‹คํ˜•์„ฑ์€ OCP์˜ ์ง์ ‘์  ๊ตฌํ˜„ โ€” if ์ง€์˜ฅ์„ ์—†์• ๋Š” ๋งˆ๋ฒ•์ด๋‹ค.

"์ƒˆ ์ข…๋ฅ˜ ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ์•ˆ ๊ฑด๋“œ๋ฆฐ๋‹ค" ๋Š” OCP๊ฐ€ ๋‹คํ˜•์„ฑ์œผ๋กœ ๊ฐ€๋Šฅ. if (type.equals("VIP")) ๊ฐ™์€ ๋ถ„๊ธฐ๋Š” ๋‹คํ˜•์„ฑ์œผ๋กœ ๋Œ€์ฒด. instanceof ๊ฐ€ ์ฝ”๋“œ์— ๋งŽ์ด ๋“ฑ์žฅํ•˜๋ฉด ์„ค๊ณ„ ์žฌ๊ฒ€ํ†  ์‹ ํ˜ธ. SOLID, Spring DI, ๋””์ž์ธ ํŒจํ„ด ๋ชจ๋‘ ๋‹คํ˜•์„ฑ ์œ„์— ์„œ ์žˆ์Œ.


๐ŸŽ“ ํ•™์Šต ์ž๊ธฐ ์ ๊ฒ€

๊ธฐ๋ณธ ์ดํ•ด

  • ๋‹คํ˜•์„ฑ์„ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • Animal a = new Dog() ๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ด์œ ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๋™์  ๋ฐ”์ธ๋”ฉ๊ณผ ์ •์  ๋ฐ”์ธ๋”ฉ์˜ ์ฐจ์ด๋ฅผ ์•ˆ๋‹ค
  • VMT์˜ ์—ญํ• ์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค

์‹ค์ „ ์ ์šฉ

  • ILIC ์ฝ”๋“œ์˜ if ์ง€์˜ฅ์„ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ฆฌํŒฉํ† ๋งํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ธํ„ฐํŽ˜์ด์Šค + ๊ตฌํ˜„์ฒด ํŒจํ„ด์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • List ๊ฐ™์€ ์ปฌ๋ ‰์…˜ ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  • @Override๋ฅผ ๋ชจ๋“  ์˜ค๋ฒ„๋ผ์ด๋“œ์— ์‚ฌ์šฉํ•œ๋‹ค

๋ฉด์ ‘ ๋Œ€๋น„ (3-5๋ถ„ ๋‹ต๋ณ€)

  • "๋‹คํ˜•์„ฑ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ
  • "๋‹คํ˜•์„ฑ์˜ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ?" (VMT, ๋™์  ๋ฐ”์ธ๋”ฉ) ๋‹ต๋ณ€ ๊ฐ€๋Šฅ
  • "ํ•„๋“œ๋„ ๋‹คํ˜•์„ฑ์ด ์ ์šฉ๋˜๋‚˜?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ (NO)
  • "static ๋ฉ”์„œ๋“œ๋Š” ๋‹คํ˜•์„ฑ?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ (NO)
  • "ILIC์—์„œ ๋‹คํ˜•์„ฑ์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜์…จ๋‚˜์š”?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ

์ž๊ธฐ ์ ๊ฒ€ ์งˆ๋ฌธ ๋‹ต๋ณ€

Q1: Animal a = new Dog(); a.eat(); ์—์„œ ํ˜ธ์ถœ๋˜๋Š” eat()์€ ๋ˆ„๊ตฌ ๊ฒƒ์ธ๊ฐ€?

Dog์˜ eat() ์ž…๋‹ˆ๋‹ค (Dog๊ฐ€ eat()์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•œ ๊ฒฝ์šฐ).

์ƒ์„ธ ์„ค๋ช…:

public class Animal {
    public void eat() { System.out.println("๋™๋ฌผ์ด ๋จน๋Š”๋‹ค"); }
}

public class Dog extends Animal {
    @Override
    public void eat() { System.out.println("๊ฐœ๊ฐ€ ๋จน๋Š”๋‹ค"); }
}

Animal a = new Dog();
a.eat();  // "๊ฐœ๊ฐ€ ๋จน๋Š”๋‹ค"

์™œ? โ€” ๋™์  ๋ฐ”์ธ๋”ฉ (Dynamic Binding) ๋•Œ๋ฌธ.

JVM ๋‚ด๋ถ€ ํ๋ฆ„:
1. a.eat() ํ˜ธ์ถœ
2. JVM: "a ๋ณ€์ˆ˜์˜ ์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…์€ Animal์ด์ง€๋งŒ..."
3. JVM: "a๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ค์ œ ๊ฐ์ฒด๋Š” ๋ฌด์—‡์ธ๊ฐ€? โ†’ Dog ์ธ์Šคํ„ด์Šค"
4. JVM: "Dog์˜ VMT์—์„œ eat() ๋ฉ”์„œ๋“œ ์ฐพ๊ธฐ"
5. VMT[eat] = Dog.eat (์˜ค๋ฒ„๋ผ์ด๋“œ๋˜์–ด ์žˆ์Œ)
6. Dog.eat() ์‹คํ–‰ โ†’ "๊ฐœ๊ฐ€ ๋จน๋Š”๋‹ค"

ํ•ต์‹ฌ ํ†ต์ฐฐ โญ :

"์ฐธ์กฐ ๋ณ€์ˆ˜์˜ ํƒ€์ž…(Animal)์ด ๋ฌด์—‡์ธ์ง€๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค. ์‹ค์ œ ๊ฐ์ฒด์˜ ํƒ€์ž…(Dog)์ด ๋ฉ”์„œ๋“œ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค."

์˜ˆ์™ธ ์ƒํ™ฉ:

๋งŒ์•ฝ Dog๊ฐ€ eat()์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด?

public class Dog extends Animal {
    // eat() ์˜ค๋ฒ„๋ผ์ด๋“œ X
}

Animal a = new Dog();
a.eat();  // "๋™๋ฌผ์ด ๋จน๋Š”๋‹ค" โ€” ์ƒ์†๋ฐ›์€ Animal.eat() ํ˜ธ์ถœ

โ†’ ์ž์‹์ด ์•ˆ ๊ฐ€์ง€๋ฉด ๋ถ€๋ชจ์˜ ๊ฒƒ์„ ์‚ฌ์šฉ (VMT์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋™์ž‘).


Q2: ์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…๊ณผ ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์˜ ์ฐจ์ด๋Š”?

์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž… = ๋ณ€์ˆ˜ ์„ ์–ธ ์‹œ ๋ช…์‹œํ•œ ํƒ€์ž… (์ฐธ์กฐ ํƒ€์ž…)
๋Ÿฐํƒ€์ž„ ํƒ€์ž… = ๋ณ€์ˆ˜๊ฐ€ ์‹ค์ œ๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด์˜ ํƒ€์ž…

Animal a = new Dog();
//  โ†‘           โ†‘
// ์ปดํŒŒ์ผ ํƒ€์ž„  ๋Ÿฐํƒ€์ž„ ํƒ€์ž…
//  ํƒ€์ž… (์ฐธ์กฐ)  (์‹ค์ œ ๊ฐ์ฒด)

๊ฐ๊ฐ์ด ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ:

์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…์ด ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ (์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ด„)

1. ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ โญ :

Animal a = new Dog();
a.eat();           // โœ… Animal์— ์žˆ์Œ
a.bark();          // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” Animal์—๋Š” bark() ์—†์Œ

โ†’ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ณ€์ˆ˜์˜ ์„ ์–ธ ํƒ€์ž…๋งŒ ๋ด„.

2. ์บ์ŠคํŒ… ๊ฐ€๋Šฅ ์—ฌ๋ถ€:

Animal a = new Dog();
Dog d = (Dog) a;     // โœ… ๋ช…์‹œ์  ์บ์ŠคํŒ… ํ•„์š”
String s = (String) a; // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” ๋ฌด๊ด€ํ•œ ํƒ€์ž…

๋Ÿฐํƒ€์ž„ ํƒ€์ž…์ด ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ (JVM์ด ๋ด„)

1. ์‹ค์ œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ (๋‹คํ˜•์„ฑ) โญ :

Animal a = new Dog();
a.eat();  // Dog.eat() ํ˜ธ์ถœ โ€” ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์œผ๋กœ ๊ฒฐ์ •

2. instanceof ๊ฒฐ๊ณผ:

Animal a = new Dog();
a instanceof Dog;     // true โ€” ๋Ÿฐํƒ€์ž„ ํƒ€์ž…
a instanceof Animal;  // true โ€” ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์˜ ๋ถ€๋ชจ
a instanceof Cat;     // false

3. ClassCastException ๋ฐœ์ƒ ์—ฌ๋ถ€:

Animal a = new Cat();
Dog d = (Dog) a;  // ์ปดํŒŒ์ผ OK, ๋Ÿฐํƒ€์ž„์— ClassCastException โš ๏ธ

โ†’ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” "Dog๋กœ ์บ์ŠคํŒ…ํ•˜๊ฒ ๋‹ค" ๋งŒ ํ™•์ธ, ์‹ค์ œ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋Š” ๋Ÿฐํƒ€์ž„.


๋น„์œ ๋กœ ์ •๋ฆฌ:

์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž… = ๋ช…ํ•จ์˜ ์ง์ฑ…
  - "๋‚˜๋Š” ๋งค๋‹ˆ์ €์ž…๋‹ˆ๋‹ค"
  - ๋ช…ํ•จ๋งŒ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด

๋Ÿฐํƒ€์ž„ ํƒ€์ž… = ์‹ค์ œ ์‚ฌ๋žŒ
  - ๊ทธ ์‚ฌ๋žŒ์˜ ์ง„์งœ ๋Šฅ๋ ฅ
  - ์‹ค์ œ๋กœ ๋งŒ๋‚˜์•ผ ์•Œ ์ˆ˜ ์žˆ์Œ

Animal a = new Dog();
๋ช…ํ•จ: "๋™๋ฌผ (Animal)"
์‹ค์ œ: ๊ฐœ (Dog)

ํ•ต์‹ฌ ์ฐจ์ด ํ‘œ:

์ธก๋ฉด์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž…๋Ÿฐํƒ€์ž„ ํƒ€์ž…
๊ฒฐ์ • ์‹œ์ ์ปดํŒŒ์ผ ์‹œ์‹คํ–‰ ์‹œ
๋ˆ„๊ฐ€ ๋ด„์ปดํŒŒ์ผ๋ŸฌJVM
๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉค๋ฒ„์‹ค์ œ ๋ฉ”์„œ๋“œ
๋ณ€๊ฒฝ ๊ฐ€๋Šฅ?NO (์„ ์–ธ ํ›„ ๊ณ ์ •)NO (๊ฐ์ฒด ์ƒ์„ฑ ํ›„ ๊ณ ์ •)

๋‹ค์Œ Unit์œผ๋กœ

  • instanceof์™€ ํ˜•๋ณ€ํ™˜ ์„ ํ•™์Šตํ•  ์ค€๋น„ ์™„๋ฃŒ
  • "๋‹คํ˜•์„ฑ์—์„œ ์ž์‹์˜ ๊ณ ์œ  ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๋ฉด?" ์ด ๊ถ๊ธˆํ•˜๋‹ค
profile
Software Developer

0๊ฐœ์˜ ๋Œ“๊ธ€