๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Java

[Java] Composition vs Inheritance — ์–ธ์ œ ์จ์•ผ ํ•˜๋‚˜?

by clolee 2025. 4. 2.
๐Ÿง  Composition vs Inheritance — ์–ธ์ œ ์จ์•ผ ํ•˜๋‚˜?

โœ… 1. ๊ธฐ๋ณธ ๊ฐœ๋… ์ •๋ฆฌ

๐Ÿ“Œ Inheritance (์ƒ์†)

  • "is-a" ๊ด€๊ณ„์ผ ๋•Œ ์‚ฌ์šฉ
  • ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋‹ค๋ฅธ ํด๋ž˜์Šค์˜ ํ•„๋“œ์™€ ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์†๋ฐ›์•„ ํ™•์žฅ
  • ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•˜๋ฉฐ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์— ์œ ๋ฆฌ
class Animal {
    void eat() { System.out.println("eating..."); }
}

class Dog extends Animal {
    void bark() { System.out.println("bark!"); }
}

โœ… Dog is-a Animal → ์˜ฌ๋ฐ”๋ฅธ ์ƒ์†


๐Ÿ“Œ Composition (๊ตฌ์„ฑ)

  • "has-a" ๊ด€๊ณ„์ผ ๋•Œ ์‚ฌ์šฉ
  • ํด๋ž˜์Šค ๋‚ด๋ถ€์— ๋‹ค๋ฅธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ•„๋“œ๋กœ ํฌํ•จ์‹œ์ผœ ์‚ฌ์šฉ
  • ์ƒ์†๋ณด๋‹ค ์œ ์—ฐํ•˜๋ฉฐ, ์—ญํ•  ๋ถ„๋ฆฌ์™€ ๊ธฐ๋Šฅ ์กฐํ•ฉ์— ์œ ๋ฆฌ
class Engine {
    void run() { System.out.println("Engine running"); }
}

class Car {
    private Engine engine = new Engine();

    void start() {
        engine.run();  // Engine์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉ
    }
}

โœ… Car has-a Engine → ๊ตฌ์„ฑ ๊ด€๊ณ„


โœ… 2. ์ฐจ์ด์  ์š”์•ฝ

ํ•ญ๋ชฉ Inheritance (์ƒ์†) Composition (๊ตฌ์„ฑ)
๊ด€๊ณ„ is-a has-a
๊ฒฐํ•ฉ๋„ ๋†’์Œ ๋‚ฎ์Œ
์œ ์—ฐ์„ฑ ๋‚ฎ์Œ ๋†’์Œ
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ๋‚ฎ์Œ ๋†’์Œ
๋ณ€๊ฒฝ ์˜ํ–ฅ ํผ (๋ถ€๋ชจ ๋ณ€๊ฒฝ ์‹œ ์ž์‹ ์˜ํ–ฅ) ์ž‘์Œ (ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ํฌํ•จ)
์‚ฌ์šฉ ์˜ˆ ํƒ€์ž… ํ™•์žฅ ๊ธฐ๋Šฅ ์กฐํ•ฉ, ์œ„์ž„

โ— 3. โŒ ์–ธ์ œ ์ƒ์†์„ ํ”ผํ•ด์•ผ ํ• ๊นŒ? + โœ… ์–ธ์ œ ์ƒ์†์„ ์จ์•ผ ํ• ๊นŒ?

โŒ ํ”ผํ•ด์•ผ ํ•  ์ƒํ™ฉ๋“ค

โŒ ์˜ˆ1: ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ ์žฌ์‚ฌ์šฉ ๋ชฉ์ ์œผ๋กœ๋งŒ ์ƒ์†ํ•  ๋•Œ

class FileLogger {
    void log(String message) {
        System.out.println("LOG: " + message);
    }
}

class User extends FileLogger { // โŒ User is-a FileLogger?
    String name;
}

๐Ÿ“Œ User๋Š” ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ธฐ ์œ„ํ•ด ์ƒ์†์„ ๋ฐ›์•˜์ง€๋งŒ User is-a FileLogger๋Š” ๋ง์ด ์•ˆ ๋จ
โžก๏ธ ์ž˜๋ชป๋œ ์ƒ์†

โœ… ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ์‹ (Composition):

class User {
    String name;
    FileLogger logger = new FileLogger();

    void doSomething() {
        logger.log("User did something");
    }
}

โŒ ์˜ˆ2: ๋ถ€๋ชจ ๋ณ€๊ฒฝ ์‹œ ์ž์‹๊นŒ์ง€ ์˜ํ–ฅ ๋ฐ›๋Š” ๊ฒฝ์šฐ

class Animal {
    void move() {
        System.out.println("Animal moves");
    }
}

class Snake extends Animal {
    // Snake๋Š” ๋ถ€๋ชจ ๋ฉ”์„œ๋“œ๋งŒ ์‚ฌ์šฉ ์ค‘
}

๋ถ€๋ชจ ์ˆ˜์ •:

class Animal {
    void move() {
        // ์ด๋™ ์‹œ ๋กœ๊น… ์ถ”๊ฐ€๋จ
        System.out.println("LOG: animal moving...");
    }
}

๐Ÿ“Œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ณ€๊ฒฝ์ด ์ž์‹ ํด๋ž˜์Šค ์ „์ฒด์— ์˜ํ–ฅ์„ ๋ฏธ์นจ
โžก๏ธ ์บก์Аํ™” ๊นจ์ง

๋”๋ณด๊ธฐ

๊ทธ ๊ตฌ์กฐ๋ฅผ ์ƒ์† ๋Œ€์‹  ๊ตฌ์„ฑ(Composition)์œผ๋กœ ๋ฐ”๊ฟจ์„ ๋•Œ ์–ด๋–ค ์‹์œผ๋กœ ๋˜๋Š”์ง€


โœ… ์ƒ์† ๊ตฌ์กฐ (๋ฌธ์ œ ์ƒํ™ฉ ๋‹ค์‹œ ๋ณต์Šต)

1. ์ƒ์† ์‹œ ๋ถ€๋ชจ ํด๋ž˜์Šค ๋ณ€๊ฒฝ → ์ž์‹ ํด๋ž˜์Šค ์˜ํ–ฅ ๋ฐ›๋Š” ์ด์œ 

 

๐Ÿ“Œ ๊ฐœ๋… ์„ค๋ช…

์ƒ์†์€ ๋ถ€๋ชจ์˜ ๋ชจ๋“  ํ•„๋“œ์™€ ๋ฉ”์„œ๋“œ๋ฅผ ์ž์‹ ํด๋ž˜์Šค๊ฐ€ ๊ทธ๋Œ€๋กœ "๋ฌผ๋ ค๋ฐ›๋Š”" ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.
์ด๋Š” ๋งค์šฐ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๋ถ€๋ชจ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ž์‹์ด ์ž๋™์œผ๋กœ ์˜ํ–ฅ์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฑธ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ(high coupling) ์ด๋ผ๊ณ  ํ•ด์š”.


โ— ๋ฌธ์ œ ์š”์•ฝ

๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์ด ๋ฐ”๋€Œ๋ฉด,
์ž์‹ ํด๋ž˜์Šค๋Š” ์•Œ์ง€ ๋ชปํ•œ ์ฑ„ ๊ทธ ์˜ํ–ฅ์„ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค.

 

๐Ÿ’ก ์˜ˆ์ œ ์„ค๋ช…

class Animal {
    void move() {
        System.out.println("Animal moves");
    }
}

class Snake extends Animal {
    // ์•„๋ฌด ๊ฒƒ๋„ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ์•ˆ ํ•จ → Animal์˜ move() ์‚ฌ์šฉ
}

 

๐Ÿ’ฅ ๊ทธ๋Ÿฐ๋ฐ Animal์˜ ๋‚ด๋ถ€๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ...

class Animal {
    void move() {
        logGPS();  // ์˜ˆ: ์ถ”๊ฐ€๋œ ๋กœ๊น… ๊ธฐ๋Šฅ (์„ฑ๋Šฅ ์ €ํ•˜ ๊ฐ€๋Šฅ์„ฑ)
        System.out.println("Animal moves");
    }
}

 

โ— ๋ฌธ์ œ

Snake s = new Snake();
s.move();  // ์ถœ๋ ฅ: Animal moves with tracking
           // → ์˜๋„์น˜ ์•Š๊ฒŒ GPS ๋กœ๊ทธ ๊ธฐ๋Šฅ๊นŒ์ง€ ๋”ฐ๋ผ์˜ด
  • Snake๋Š” move() ๋‚ด๋ถ€๊ฐ€ ๋ณ€๊ฒฝ๋œ ์‚ฌ์‹ค๋„ ๋ชจ๋ฅด๊ณ  ๋”ฐ๋ผ์˜ด
  • ํ…Œ์ŠคํŠธ๋„ ์˜ˆ์™ธ ์—†์ด ์˜ํ–ฅ๋ฐ›๊ณ , ์„ฑ๋Šฅ ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ

๐Ÿ” ์š”์•ฝ: ์ƒ์†์€ ๋‚ด๋ถ€ ๊ตฌํ˜„๊นŒ์ง€ ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๋ฏ€๋กœ, ๋ถ€๋ชจ ๋ณ€๊ฒฝ์ด ์ž์‹์—๊ฒŒ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ๋œ๋‹ค.

โžก๏ธ ์ด๋Ÿฐ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ๊ตฌ์„ฑ(Composition)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜๋‹ค.


โœ… ๊ตฌ์„ฑ(Composition) ๊ตฌ์กฐ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋ฉด?

class AnimalMover {
    void move() {
        System.out.println("Animal moves");
    }
}

class Snake {
    private AnimalMover mover = new AnimalMover();

    void move() {
        mover.move();
    }
}

๐Ÿ”„ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด…

  • Snake๋Š” AnimalMover ๊ฐ์ฒด๋ฅผ "๊ฐ€์ง€๊ณ " ์žˆ์–ด์š” (has-a ๊ด€๊ณ„)
  • AnimalMover๊ฐ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„, Snake๋Š” ์„ ํƒ์ ์œผ๋กœ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์–ด์š”

โœ… ๊ตฌ์„ฑ์˜ ํ•ต์‹ฌ ์žฅ์ : ์œ ์—ฐํ•˜๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ!

class AnimalMoverWithLogging extends AnimalMover {
    @Override
    void move() {
        logGPS();  // ์„ฑ๋Šฅ ๋น„์šฉ์ด ํฐ ๊ธฐ๋Šฅ
        super.move();
    }
}
Snake snake = new Snake();
snake.setMover(new AnimalMoverWithLogging());  // ์ „๋žต ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

โžก๏ธ ๋Ÿฐํƒ€์ž„์— ์–ด๋–ค mover๋ฅผ ์“ธ์ง€ ๊ต์ฒด ๊ฐ€๋Šฅ → ์ „๋žต ํŒจํ„ด ์‘์šฉ ๊ฐ€๋Šฅ


โœ… ์ „์ฒด ์ฝ”๋“œ ์˜ˆ์‹œ (๋ฆฌํŒฉํ† ๋ง ๋ฒ„์ „)

// ์›€์ง์ž„์„ ๋‹ด๋‹นํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ
class AnimalMover {
    void move() {
        System.out.println("Animal moves");
    }
}

// ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ์ „๋žต
class AnimalMoverWithLogging extends AnimalMover {
    @Override
    void move() {
        logGPS();
        super.move();
    }

    private void logGPS() {
        System.out.println("[GPS ๋กœ๊ทธ]: ์œ„์น˜ ๊ธฐ๋ก๋จ");
    }
}

// Snake๋Š” Animal์ด ์•„๋‹Œ ๋ณ„๋„ ํด๋ž˜์Šค → ๊ตฌ์„ฑ ์‚ฌ์šฉ
class Snake {
    private AnimalMover mover;

    Snake(AnimalMover mover) {
        this.mover = mover;
    }

    void move() {
        mover.move();  // ๋™์ž‘ ์œ„์ž„
    }

    void setMover(AnimalMover newMover) {
        this.mover = newMover;  // ์ „๋žต ๊ต์ฒด๋„ ๊ฐ€๋Šฅ
    }
}

โœ… ์‹คํ–‰ ์˜ˆ์‹œ

public class Main {
    public static void main(String[] args) {
        Snake snake = new Snake(new AnimalMover());
        snake.move();
        // ์ถœ๋ ฅ: Animal moves

        // ๋™์ž‘ ์ „๋žต ๊ต์ฒด
        snake.setMover(new AnimalMoverWithLogging());
        snake.move();
        // ์ถœ๋ ฅ:
        // [GPS ๋กœ๊ทธ]: ์œ„์น˜ ๊ธฐ๋ก๋จ
        // Animal moves
    }
}

โœ… ๊ตฌ์„ฑ์œผ๋กœ ๋ฐ”๊พธ๋ฉด ์ƒ๊ธฐ๋Š” ์žฅ์ 

ํ•ญ๋ชฉ  ์„ค๋ช…
์œ ์—ฐ์„ฑ ํ•„์š”ํ•  ๋•Œ ๋™์ž‘์„ ๊ต์ฒด ๊ฐ€๋Šฅ (์˜ˆ: ํ…Œ์ŠคํŠธ์šฉ, ์„ฑ๋Šฅ ์ œํ•œ์šฉ ๋“ฑ)
ํ…Œ์ŠคํŠธ์„ฑ ๊ฐ€์งœ mover ๋„ฃ์–ด์„œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ (MockMover)
๊ฒฐํ•ฉ๋„ ๋‚ฎ์Œ Snake๋Š” mover์˜ ์ธํ„ฐํŽ˜์ด์Šค์—๋งŒ ์˜์กด
์žฌ์‚ฌ์šฉ์„ฑ AnimalMover๋ฅผ ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ๋„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๋ณ€๊ฒฝ ์•ˆ์ „์„ฑ ๋ถ€๋ชจ ํด๋ž˜์Šค ๋ณ€ํ™”์— ์ž๋™์œผ๋กœ ์˜ํ–ฅ๋ฐ›์ง€ ์•Š์Œ

๐Ÿง  ๊ฒฐ๋ก 

์ƒ์† ๊ตฌ์กฐ์—์„œ๋Š” ๋ถ€๋ชจ ํด๋ž˜์Šค ๋ณ€๊ฒฝ์ด ์ž์‹์—๊ฒŒ ์ž๋™ ์ „ํŒŒ๋˜์–ด ์œ„ํ—˜ํ•˜์ง€๋งŒ,
๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋Šฅ์„ ๊ฐ์ฒด๋กœ ๋ถ„๋ฆฌํ•ด ์„ ํƒ์ ์œผ๋กœ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ๊ต์ฒด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—
ํ›จ์”ฌ ์•ˆ์ •์ ์ด๊ณ  ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 


โŒ ์˜ˆ3: ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€ (์ƒ์† ๊นŠ์ด๋กœ ์ธํ•ด)

class UserService extends DatabaseService {
    void registerUser() {
        // DB ์—ฐ๊ฒฐ๋ถ€ํ„ฐ ๊ฐ•์ œ๋กœ ํฌํ•จ๋จ
    }
}

โœ… ๊ตฌ์„ฑ์œผ๋กœ ๋Œ€์ฒด:

class UserService {
    private DatabaseService db;

    UserService(DatabaseService db) {
        this.db = db; // ์˜์กด์„ฑ ์ฃผ์ž…
    }
}

โžก๏ธ ์œ ๋‹› ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ, ์œ ์—ฐ์„ฑ ↑


โœ… ๊ทธ๋Ÿผ ์–ธ์ œ ์ƒ์†์„ ์จ์•ผ ํ• ๊นŒ?

โœ… ์˜ˆ1: ์ง„์งœ is-a ๊ด€๊ณ„์ผ ๋•Œ

class Vehicle {
    void move() { System.out.println("Vehicle moving"); }
}

class Bus extends Vehicle {
    void openDoors() { System.out.println("Doors opened"); }
}

โžก๏ธ Bus is-a Vehicle → ๋…ผ๋ฆฌ์  ํƒ€์ž… ํ™•์žฅ์œผ๋กœ ์ ์ ˆ


โœ… ์˜ˆ2: ๊ธฐ๋ณธ ๋™์ž‘์„ ํ™•์žฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ

class Button {
    void click() {
        System.out.println("Button clicked");
    }
}

class AnimatedButton extends Button {
    @Override
    void click() {
        super.click();
        System.out.println("...with animation");
    }
}

โžก๏ธ ๋ถ€๋ชจ ๊ธฐ๋ณธ ๋™์ž‘์„ ํ™•์žฅํ•ด์„œ ์“ฐ๋Š” ๊ฒฝ์šฐ๋Š” ์ƒ์†์ด ์ ํ•ฉ


โœ… 4. ์–ธ์ œ Composition์„ ์จ์•ผ ํ• ๊นŒ?

โœ… ์˜ˆ1: ๊ธฐ๋Šฅ ์กฐํ•ฉ์ด ๋ชฉ์ ์ผ ๋•Œ

class NotificationSender {
    void send(String msg) {
        System.out.println("Sending: " + msg);
    }
}

class OrderService {
    NotificationSender sender = new NotificationSender();

    void completeOrder() {
        sender.send("Order completed!");
    }
}

โžก๏ธ OrderService is-a Sender๋Š” ์•„๋‹˜ → ๊ธฐ๋Šฅ๋งŒ ํฌํ•จํ•ด์„œ ์‚ฌ์šฉ


โœ… ์˜ˆ2: ๋™์ž‘์„ ์œ ์—ฐํ•˜๊ฒŒ ๊ต์ฒดํ•ด์•ผ ํ•  ๋•Œ (์ „๋žต ํŒจํ„ด)

interface PayStrategy {
    void pay(int amount);
}

class KakaoPay implements PayStrategy {
    public void pay(int amount) {
        System.out.println("Paid with KakaoPay");
    }
}

class PaymentProcessor {
    PayStrategy strategy;

    PaymentProcessor(PayStrategy strategy) {
        this.strategy = strategy;
    }

    void process(int amount) {
        strategy.pay(amount);
    }
}

โžก๏ธ ๋Ÿฐํƒ€์ž„์— ์ „๋žต(๊ฒฐ์ œ ์ˆ˜๋‹จ) ๊ต์ฒด ๊ฐ€๋Šฅ → ๊ตฌ์„ฑ ๋ฐฉ์‹์˜ ๋Œ€ํ‘œ์  ์žฅ์ 

๋”๋ณด๊ธฐ

โœ… ์ „๋žต ํŒจํ„ด์„ ํ†ตํ•ด ๊ตฌ์„ฑ(Composition)์œผ๋กœ ๋™์ž‘์„ ์œ ์—ฐํ•˜๊ฒŒ ๊ต์ฒดํ•˜๋Š” ๋ฐฉ์‹

 

๐Ÿ“Œ ๊ฐœ๋… ์„ค๋ช…

๋•Œ๋•Œ๋กœ ์šฐ๋ฆฌ๋Š” ๊ฐ™์€ ๋™์ž‘์„ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด:

  • ์˜ค๋Š˜์€ ์นด์นด์˜คํŽ˜์ด๋กœ ๊ฒฐ์ œ
  • ๋‚ด์ผ์€ ์‹ ์šฉ์นด๋“œ๋กœ ๊ฒฐ์ œ
  • ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฐ€์งœ ๊ฒฐ์ œ๋กœ ์ฒ˜๋ฆฌ

์ด๋•Œ ์ƒ์†์œผ๋กœ๋Š” ๋™์ž‘ ๊ต์ฒด๊ฐ€ ์–ด๋ ต๊ณ ,
์ธํ„ฐํŽ˜์ด์Šค + ๊ตฌ์„ฑ(Composition) ์„ ํ™œ์šฉํ•˜๋ฉด ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โžก๏ธ ์ด ๋ฐฉ์‹์ด ์ „๋žต ํŒจํ„ด (Strategy Pattern) ์ž…๋‹ˆ๋‹ค.


๐ŸŽฏ ์ „๋žต ํŒจํ„ด ๊ฐœ๋… ์š”์•ฝ


์š”์†Œ  ์—ญํ• 
์ธํ„ฐํŽ˜์ด์Šค ๊ณตํ†ต ๋™์ž‘์˜ "๊ณ„์•ฝ" ์ •์˜ (ex. pay)
๊ตฌํ˜„ ํด๋ž˜์Šค ๊ฐ๊ฐ์˜ ์ „๋žต์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ (ex. KakaoPay, CardPay)
Context ํด๋ž˜์Šค ๋™์ž‘์„ ์‹คํ–‰ํ•˜์ง€๋งŒ ์ „๋žต์€ "๋ฐ”๊นฅ์—์„œ ์ฃผ์ž…"๋ฐ›์Œ

โœ… ์˜ˆ์ œ ์ฝ”๋“œ

 

1) ๊ณตํ†ต ๋™์ž‘ ์ •์˜ (์ธํ„ฐํŽ˜์ด์Šค)

interface PayStrategy {
    void pay(int amount);
}

 

2) ์ „๋žต ๊ตฌํ˜„์ฒด (์—ฌ๋Ÿฌ ๊ฐœ)

class KakaoPay implements PayStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " with KakaoPay");
    }
}

class CardPay implements PayStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " with Credit Card");
    }
}

 

3) ์‹คํ–‰ ํด๋ž˜์Šค (์ „๋žต์„ ๋ฐ›์•„์„œ ์œ„์ž„)

class PaymentProcessor {
    private PayStrategy strategy;

    public PaymentProcessor(PayStrategy strategy) {
        this.strategy = strategy;
    }

    public void process(int amount) {
        strategy.pay(amount);  // ์ „๋žต์—๊ฒŒ ์‹คํ–‰ ์œ„์ž„
    }
}

๐Ÿงช ์‚ฌ์šฉ ์˜ˆ์‹œ

PayStrategy strategy = new KakaoPay(); // ์ „๋žต ์„ ํƒ
PaymentProcessor processor = new PaymentProcessor(strategy);

processor.process(10000);  // ์ถœ๋ ฅ: Paid 10000 with KakaoPay

// ์ „๋žต ๊ต์ฒด
processor = new PaymentProcessor(new CardPay());
processor.process(10000);  // ์ถœ๋ ฅ: Paid 10000 with Credit Card

๐Ÿ’ก ์ด ๊ตฌ์กฐ์˜ ์žฅ์ 


์žฅ์  ์„ค๋ช…
์œ ์—ฐ์„ฑ ์‹คํ–‰ ์ค‘ ์ „๋žต(๋™์ž‘)์„ ์‰ฝ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ
ํ…Œ์ŠคํŠธ ์šฉ์ด ๋ชจ์˜ ์ „๋žต(MockStrategy)์œผ๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
์žฌ์‚ฌ์šฉ์„ฑ ์ „๋žต ๊ตฌํ˜„์ฒด๋“ค์„ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋„ ํ™œ์šฉ ๊ฐ€๋Šฅ
๊ฒฐํ•ฉ๋„ ๋‚ฎ์Œ ์‹คํ–‰ ํด๋ž˜์Šค๋Š” PayStrategy์—๋งŒ ์˜์กดํ•˜๊ณ , ๊ตฌ์ฒด ๊ตฌํ˜„์ฒด๋Š” ๋ชฐ๋ผ๋„ ๋จ

๐Ÿ“Œ ์ •๋ฆฌ: ์ „๋žต ํŒจํ„ด์ด ํ•„์š”ํ•œ ์ƒํ™ฉ

  • ํŠน์ • ๊ธฐ๋Šฅ(์˜ˆ: ๊ฒฐ์ œ, ์ •๋ ฌ, ์ถœ๋ ฅ)์„ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๋•Œ
  • ์‹คํ–‰ ์ค‘์— ์ „๋žต์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์„ ๋•Œ
  • ํ…Œ์ŠคํŠธ ์‹œ ์‹ค์ œ ์ „๋žต ๋Œ€์‹  ๊ฐ€์งœ(Mock)๋ฅผ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ
  • ์ƒ์†๋ณด๋‹ค ๋” ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ

โœ… ๊ฒฐ๋ก  ์š”์•ฝ

ํ•ญ๋ชฉ  ์ƒ์† (Inheritance)  ๊ตฌ์„ฑ (Composition)
๊ตฌ์กฐ ๊ณ„์ธต์  (is-a) ์กฐํ•ฉ์  (has-a, uses-a)
๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ๊ฐ•๊ฑดํ•จ ์•ฝํ•จ (๋ถ€๋ชจ ๋ณ€๊ฒฝ → ์ž์‹ ์˜ํ–ฅ) ๊ฐ•ํ•จ (๋‚ด๋ถ€ ๊ตฌ์„ฑ๋งŒ ๊ต์ฒด)
๋™์ž‘ ํ™•์žฅ ์ƒ์† or ์˜ค๋ฒ„๋ผ์ด๋”ฉ ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ์ „๋žต ๊ต์ฒด
์œ ์—ฐ์„ฑ ๋‚ฎ์Œ โœ… ๋†’์Œ
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ์–ด๋ ค์›€ โœ… ๋†’์Œ

 


โœ… ์˜ˆ3: ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

class ReportGenerator {
    FileWriter writer;

    ReportGenerator(FileWriter writer) {
        this.writer = writer;
    }

    void generate() {
        writer.write("Report...");
    }
}

โžก๏ธ ์‹ค์ œ ์‹คํ–‰์—์„œ๋Š” FileWriter, ํ…Œ์ŠคํŠธ์—์„œ๋Š” MockWriter ์‚ฌ์šฉ ๊ฐ€๋Šฅ


๐ŸŽฏ ๊ฒฐ๋ก  ์š”์•ฝ: ์–ธ์ œ ์–ด๋–ค ๊ฑธ ์จ์•ผ ํ•˜๋‚˜?

์ƒํ™ฉ ์ƒ์† (Inheritance) ๊ตฌ์„ฑ (Composition)
์ง„์งœ๋กœ is-a ๊ด€๊ณ„์ผ ๋•Œ โœ… ์‚ฌ์šฉ โŒ ๋ถˆํ•„์š”
๊ธฐ๋Šฅ ์žฌ์‚ฌ์šฉ์ด ๋ชฉ์ ์ผ ๋•Œ โŒ ์œ„ํ—˜ โœ… ์œ ๋ฆฌ
์œ ์—ฐํ•˜๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅํ•ด์•ผ ํ•  ๋•Œ โŒ ๋ถˆํŽธ โœ… ๊ฐ€๋Šฅ
ํ…Œ์ŠคํŠธ, ํ™•์žฅ ๊ณ ๋ ค ์‹œ โŒ ๋‹จ์  ๋งŽ์Œ โœ… ์žฅ์  ๋งŽ์Œ
๊ด€๊ณ„๊ฐ€ has-a / uses-a โŒ ๋ถ€์ ์ ˆ โœ… ์ ์ ˆ
๋Œ€ํ‘œ ์ ์šฉ ํŒจํ„ด ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด ์ „๋žต ํŒจํ„ด, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ, DI

๐Ÿง  ํ•ต์‹ฌ ์ฒ ํ•™

โœ… "๊ฐ€๋Šฅํ•œ ํ•œ ์ƒ์†๋ณด๋‹ค ๊ตฌ์„ฑ์„ ์„ ํ˜ธํ•˜๋ผ (Favor composition over inheritance)"
— Effective Java, Joshua Bloch


๋Œ“๊ธ€