๐Ÿ“• [Object] 11์žฅ. ํ•ฉ์„ฑ๊ณผ ์œ ์—ฐํ•œ ์„ค๊ณ„

๋ฐ•์ƒ๋ฏผยท2024๋…„ 3์›” 19์ผ

Book

๋ชฉ๋ก ๋ณด๊ธฐ
17/22
post-thumbnail

์ƒ์† ๊ด€๊ณ„๋Š” is-a ๊ด€๊ณ„๋ผ๊ณ  ๋ถ€๋ฅด๊ณ  ํ•ฉ์„ฑ ๊ด€๊ณ„๋Š” has-a ๊ด€๊ณ„๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

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

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

โญ๏ธ ์ƒ์†์„ ํ•ฉ์„ฑ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ

์ด์ „์— ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•ด ์ƒ์†์„ ๋‚จ์šฉ ํ–ˆ์„ ๋•Œ ์ง๋ฉดํ•  ์ˆ˜ ์žˆ๋Š” ์„ธ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์„ ์‚ดํŽด๋ดค๋‹ค.

  • ๋ถˆํ•„์š”ํ•œ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์† ๋ฌธ์ œ : java.util.Properties์™€ java.util.Stack

  • ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋ผ์ด๋”ฉ์˜ ์˜ค์ž‘์šฉ ๋ฌธ์ œ : java.util.HashSet์„ ์ƒ์†๋ฐ›์€ InstumentedHashSet

  • ๋ถ€๋ชจ ํด๋ž˜์Šค์™€ ์ž์‹ ํด๋ž˜์Šค์˜ ๋™์‹œ ์ˆ˜์ • ๋ฌธ์ œ : Playlist๋ฅผ ์ƒ์†๋ฐ›์€ PersonalPlaylist

๐Ÿ“Œ ์ƒ์†์œผ๋กœ ์ธํ•œ ์กฐํ•ฉ์˜ ํญ๋ฐœ์ ์ธ ์ฆ๊ฐ€

์ƒ์†์œผ๋กœ ์ธํ•ด ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ง€๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค.

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

์ด์ฒ˜๋Ÿผ ์ƒ์†์˜ ๋‚จ์šฉ์œผ๋กœ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š” ์ด์ƒ์œผ๋กœ ๋งŽ์€ ์ˆ˜์˜ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๊ฐ€๋ฆฌ์ผœ ํด๋ž˜์Šค ํญ๋ฐœ(class explosion) ๋ฌธ์ œ ๋˜๋Š” ์กฐํ•ฉ์˜ ํญ๋ฐœ(combinational explosion) ๋ฌธ์ œ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

์ƒ์† ๊ด€๊ณ„๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„์— ๊ฒฐ์ •๋˜๊ณ  ๊ณ ์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋„์ค‘์—๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์กฐํ•ฉํ•ด์•ผ ํ•˜๋Š” ์„ค๊ณ„์— ์ƒ์†์„ ์ด์šฉํ•˜๋ฉด ๋ชจ๋“  ์กฐํ•ฉ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ๋ณ„๋กœ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ ํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ“Œ ํ•ฉ์„ฑ ๊ด€๊ณ„๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ

ํด๋ž˜์Šค ํญ๋ฐœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•ฉ์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋Ÿฐํƒ€์ž„์— ๊ฐ์ฒด ์‚ฌ์ด์˜ ์˜์กด์„ฑ์„ ์ž์œ ๋กญ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ•ฉ์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌํ˜„ ์‹œ์ ์— ์ •์ฑ…๋“ค์˜ ๊ด€๊ณ„๋ฅผ ๊ณ ์ •์‹œํ‚ฌ ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ ์‹คํ–‰ ์‹œ์ ์— ์ •์ฑ…๋“ค์˜ ๊ด€๊ณ„๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

Phone ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

public class Phone {
    private RatePolicy ratepolicy;
    private List<Call> calls = new ArrayList<>();

    public Phone(RatePolicy ratePolicy) {
        this.ratePolicy = ratePolicy;
    }

    public List<Call> getCalls() {
        return Collections.unmodifiableList(calls);
    }

    public Money calculateFee() {
        return ratePolicy.calculateFee(this);
    }
}

Phone ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” RatePolicy ํƒ€์ž…์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •์˜ํ•œ๋‹ค.

public interface RatePolicy {
    Money calculateFee(Phone phone);
}

RatePolicy๋ฅผ ๊ตฌํ˜„ํ•œ ์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

public abstract class BasicRatePolicy implements RatePolicy {
    @Override
    public Money calculateFee(Phone phone) {
        Money result = Money.ZERO;

        for(Call call : phone.getCalls()) {
            result.plus(calculateCallFee(call));
        }

        return result;
    }

    abstract protected Money calculateCallFee(Call call);
}

์ถ”์ƒ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•œ๋‹ค.

public class RegularPolicy extends BasicRatePolicy {
    private Money amout;
    private Duration seconds;

    public RegularPolicy(Money amount, Duration seconds) {
        this.amount = amount;
        this.seconds = seconds;
    }

    @Override
    protected Money calculateCallFee(Call call) {
        return amount.times(call.getDuration().getSeconds() / seconds.getSeconds());
    }
}

์ด๋ฒˆ์—” ๋ถ€๊ฐ€ ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•  ์ถ”์ƒํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด๋ณด์ž

public abstract class AdditionalRatePolicy implements RatePolicy {
    private RatePolicy next;

    public AdditionalRatePolicy(RatePolicy next) {
        this.next = next;
    }

    @Override
    public Money calculateFee(Phone phone) {
        Money fee = next.calculateFee(phone);

        return afterCalculated(fee);
    }

    abstract protected Money afterCalculated(Call call);
}

์‹ค์ œ๋กœ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ๋Ÿฐํƒ€์ž„์‹œ์— ๋‹ค์–‘ํ•œ ์กฐํ•ฉ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

Phone phone = new Phone( new TexablePolicy(0.05,
                           new RateDiscountablePolicy(Money.wons(1000),
                             new RegularPolicy(...)));

โญ๏ธ ๋ฏน์Šค์ธ

๋ฏน์Šค์ธ(mixin)์€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ฝ”๋“œ ์ผ๋ถ€๋ฅผ ํด๋ž˜์Šค ์•ˆ์— ์„ž์–ด ๋„ฃ์–ด ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ฒ•์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์šฉ์–ด๋‹ค. ํ•ฉ์„ฑ์ด ์‹คํ–‰ ์‹œ์ ์— ๊ฐ์ฒด๋ฅผ ์กฐํ•ฉํ•˜๋Š” ์žฌ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์ด๋ผ๋ฉด ๋ฏน์Šค์ธ์€ ์ปดํŒŒ์ผ ์‹œ์ ์— ํ•„์š”ํ•œ ์ฝ”๋“œ ์กฐ๊ฐ์„ ์กฐํ•ฉํ•˜๋Š” ์žฌ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์ด๋‹ค.

๐Ÿ“Œ ๋ฏน์Šค์ธ์€ ์ƒ์†๊ณผ ๋‹ค๋ฅด๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์„ค๋ช…์„ ๋“ฃ๊ณ  ๋‚˜๋ฉด ๋ฏน์Šค์ธ๊ณผ ์ƒ์†์ด ์œ ์‚ฌํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒ ์ง€๋งŒ ๋ฏน์Šค์ธ์€ ์ƒ์†๊ณผ๋Š” ๋‹ค๋ฅด๋‹ค. ์ƒ์†์˜ ์ง„์ •ํ•œ ๋ชฉ์ ์€ ์ž์‹ ํด๋ž˜์Šค๋ฅผ ๋ถ€๋ชจ ํด๋ž˜์Šค์™€ ๋™์ผํ•œ ๊ฐœ๋…์ ์ธ ๋ฒ”์ฃผ๋กœ ๋ฌถ์–ด is-a ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๋ฐ˜๋ฉด ๋ฏน์Šค์ธ์€ ๋ง ๊ทธ๋Œ€๋กœ ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅธ ์ฝ”๋“œ ์•ˆ์— ์„ž์–ด ๋„ฃ๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.


์ถœ์ฒ˜
์˜ค๋ธŒ์ ํŠธ - ์ฝ”๋“œ๋กœ ์ดํ•ดํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ์„ค๊ณ„
https://github.com/eternity-oop/object

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