SOLID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ์—ฐํ•œ ์ฝ”๋“œ ์ž‘์„ฑ

SOLID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ์—ฐํ•œ ์ฝ”๋“œ ์ž‘์„ฑ

๋ฒˆ์—ญ์ž: ๋‹น์‹ ์„ ์œ„ํ•ด ์ถœํŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค Severin Perez์˜ ๊ธฐ์‚ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ SOLID ์›์น™์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์„ธ์š”. ์ด ๊ธฐ์‚ฌ์˜ ์ •๋ณด๋Š” ์ดˆ๋ณด์ž์™€ ์ˆ™๋ จ๋œ ํ”„๋กœ๊ทธ๋ž˜๋จธ ๋ชจ๋‘์—๊ฒŒ ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์— ์ข…์‚ฌํ•˜๊ณ  ์žˆ๋‹ค๋ฉด SOLID ์›์น™์— ๋Œ€ํ•ด ๋“ค์–ด๋ณด์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ๊นจ๋—ํ•˜๊ณ  ์ฒด๊ณ„์ ์ด๋ฉฐ ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—๋Š” ํŠน์ • ์ž‘์—…์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์žˆ๋‹ค๋Š” ์ ์€ ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „๋ฌธ๊ฐ€๋งˆ๋‹ค "์˜ฌ๋ฐ”๋ฅธ ๊ธธ"์— ๋Œ€ํ•œ ์ƒ๊ฐ๊ณผ ์ดํ•ด๊ฐ€ ๋‹ค๋ฅด๋ฉฐ, ์ด๋Š” ๋ชจ๋‘ ๊ฐ ๊ฐœ์ธ์˜ ๊ฒฝํ—˜์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ SOLID์—์„œ ์„ ์–ธ๋œ ์•„์ด๋””์–ด๋Š” IT ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๊ฑฐ์˜ ๋ชจ๋“  ๋Œ€ํ‘œ์ž๋“ค์— ์˜ํ•ด ๋ฐ›์•„๋“ค์—ฌ์ง‘๋‹ˆ๋‹ค. ์ด๋Š” ๋งŽ์€ ํ›Œ๋ฅญํ•œ ๊ฐœ๋ฐœ ๊ด€๋ฆฌ ๊ด€ํ–‰์˜ ์ถœํ˜„๊ณผ ๋ฐœ์ „์„ ์œ„ํ•œ ์ถœ๋ฐœ์ ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

SOLID ์›์น™์ด ๋ฌด์—‡์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ์—๊ฒŒ ์–ด๋–ป๊ฒŒ ๋„์›€์ด ๋˜๋Š”์ง€ ์ดํ•ดํ•ด ๋ด…์‹œ๋‹ค.

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์‹ค๊ธฐ ์ฝ”์Šค "๋ชจ๋ฐ”์ผ ๊ฐœ๋ฐœ์ž PRO".

์•Œ๋ฆผ: "Habr"์˜ ๋ชจ๋“  ๋…์ž๋ฅผ ์œ„ํ•œ - "Habr" ํ”„๋กœ๋ชจ์…˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Skillbox ๊ณผ์ •์— ๋“ฑ๋กํ•  ๋•Œ 10 ๋ฃจ๋ธ” ํ• ์ธ.

์†”๋ฆฌ๋“œ๋ž€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

์ด ์šฉ์–ด๋Š” ์•ฝ์–ด์ด๋ฉฐ ์šฉ์–ด์˜ ๊ฐ ๋ฌธ์ž๋Š” ํŠน์ • ์›์น™ ์ด๋ฆ„์˜ ์‹œ์ž‘ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

  • S๋‹จ์ผ ์ฑ…์ž„ ์›์น™. ๋ชจ๋“ˆ์—๋Š” ๋ณ€๊ฒฝ ์ด์œ ๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • XNUMXD๋ดํƒˆ์˜ OํŽœ/ํ์‡„์›๋ฆฌ (๊ฐœ๋ฐฉ/ํ์‡„ ์›์น™). ํด๋ž˜์Šค์™€ ๊ธฐํƒ€ ์š”์†Œ๋Š” ํ™•์žฅ์„ ์œ„ํ•ด ์—ด๋ ค์•ผ ํ•˜์ง€๋งŒ ์ˆ˜์ •์„ ์œ„ํ•ด์„œ๋Š” ๋‹ซํ˜€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • โ€ŠXNUMXD๋ดํƒˆ์˜ Liskov ๋Œ€์ฒด ์›๋ฆฌ (๋ฆฌ์Šค์ฝ”ํ”„ ๋Œ€์ฒด ์›๋ฆฌ). ๊ธฐ๋ณธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์ด๋ฅผ ์•Œ์ง€ ๋ชปํ•œ ์ฑ„ ๊ธฐ๋ณธ ์œ ํ˜•์˜ ํ•˜์œ„ ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • XNUMXD๋ดํƒˆ์˜ I์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™โ€Š (์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›๋ฆฌ). ์†Œํ”„ํŠธ์›จ์–ด ์—”ํ„ฐํ‹ฐ๋Š” ์ž์‹ ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.
  • XNUMXD๋ดํƒˆ์˜ D์˜์กด์„ฑ ๋ฐ˜์ „ ์›๋ฆฌ (์˜์กด์„ฑ ์—ญ์ „์˜ ์›๋ฆฌ) ๋†’์€ ์ˆ˜์ค€์˜ ๋ชจ๋“ˆ์€ ๋‚ฎ์€ ์ˆ˜์ค€์˜ ๋ชจ๋“ˆ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

๋‹จ์ผ ์ฑ…์ž„ ์›์น™

โ€Š
SRP(๋‹จ์ผ ์ฑ…์ž„ ์›์น™)๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐ ํด๋ž˜์Šค๋‚˜ ๋ชจ๋“ˆ์ด ํ•ด๋‹น ํ”„๋กœ๊ทธ๋žจ ๊ธฐ๋Šฅ์˜ ํ•œ ๋ถ€๋ถ„๋งŒ ๋‹ด๋‹นํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ์ฑ…์ž„์˜ ์š”์†Œ๋Š” ๊ด€๋ จ ์—†๋Š” ํด๋ž˜์Šค์— ๋ถ„์‚ฐ๋˜๊ธฐ๋ณด๋‹ค๋Š” ์ž์ฒด ํด๋ž˜์Šค์— ํ• ๋‹น๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. SRP์˜ ๊ฐœ๋ฐœ์ž์ด์ž ์ˆ˜์„ ์ „๋„์‚ฌ์ธ Robert S. Martin์€ ์ฑ…์ž„์„ ๋ณ€ํ™”์˜ ์ด์œ ๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Š” ์›๋ž˜ ์ด ์šฉ์–ด๋ฅผ ๊ทธ์˜ ์ž‘ํ’ˆ "๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„์˜ ์›๋ฆฌ"์˜ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๋กœ ์ œ์•ˆํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฐœ๋…์€ ์ด์ „์— Tom DeMarco๊ฐ€ ์ •์˜ํ•œ ์—ฐ๊ฒฐ ํŒจํ„ด์˜ ๋Œ€๋ถ€๋ถ„์„ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฐœ๋…์—๋Š” David Parnas๊ฐ€ ๊ณต์‹ํ™”ํ•œ ์—ฌ๋Ÿฌ ๊ฐœ๋…๋„ ํฌํ•จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ์ฃผ์š”ํ•œ ๊ฒƒ์€ ์บก์Šํ™”์™€ ์ •๋ณด ์€๋‹‰์ž…๋‹ˆ๋‹ค. ํŒŒ๋ฅด๋‚˜์Šค๋Š” ์‹œ์Šคํ…œ์„ ๋ณ„๋„์˜ ๋ชจ๋“ˆ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด ๋ธ”๋ก ๋‹ค์ด์–ด๊ทธ๋žจ์ด๋‚˜ ์‹คํ–‰ ํ๋ฆ„ ๋ถ„์„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๊ณ  ์ฃผ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ชจ๋“ˆ์—๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ตœ์†Œํ•œ์˜ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ํŠน์ • ์†”๋ฃจ์…˜์ด ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ Martin์€ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๋ชฉ์ ์œผ๋กœ ํŠน์ • ๋น„์ฆˆ๋‹ˆ์Šค ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํšŒ์‚ฌ์˜ ๊ณ ์œ„ ๊ด€๋ฆฌ์ž(COO, CTO, CFO)์™€ ํ•จ๊ป˜ ํฅ๋ฏธ๋กœ์šด ์˜ˆ๋ฅผ ์ œ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด๋“ค ์ค‘ ๋ˆ„๊ตฌ๋ผ๋„ ๋‹ค๋ฅธ ๊ด€๋ฆฌ์ž์˜ ์ด์ต์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ  ์†Œํ”„ํŠธ์›จ์–ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ ์„ฑํ•œ ๋Œ€์ƒ

์–ธ์ œ๋‚˜ ๊ทธ๋ ‡๋“ฏ์ด SRP๋ฅผ ๋ฐฐ์šฐ๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์‹ค์ œ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ชจ์Šต์„ ๋ณด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™์„ ๋”ฐ๋ฅด์ง€ ์•Š๋Š” ํ”„๋กœ๊ทธ๋žจ ์„น์…˜์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์šฐ์ฃผ ์ •๊ฑฐ์žฅ์˜ ๋™์ž‘๊ณผ ์†์„ฑ์„ ์„ค๋ช…ํ•˜๋Š” Ruby ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์˜ˆ์‹œ๋ฅผ ๊ฒ€ํ† ํ•˜๊ณ  ๋‹ค์Œ ์‚ฌํ•ญ์„ ๊ฒฐ์ •ํ•ด ๋ณด์„ธ์š”.
SpaceStation ํด๋ž˜์Šค์— ์„ ์–ธ๋œ ๊ฐ์ฒด์˜ ์ฑ…์ž„์ž…๋‹ˆ๋‹ค.
์šฐ์ฃผ์ •๊ฑฐ์žฅ ์šด์˜์— ๊ด€์‹ฌ์ด ์žˆ์œผ์‹  ๋ถ„๋“ค.

class SpaceStation
  def initialize
    @supplies = {}
    @fuel = 0
  end
 
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @fuel >= 10
      puts "Thrusting action successful."
      @fuel -= 10
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

์‚ฌ์‹ค, ์šฐ๋ฆฌ ์šฐ์ฃผ ์ •๊ฑฐ์žฅ์€ ๊ธฐ๋Šฅ ์žฅ์• ๊ฐ€ ์žˆ์ง€๋งŒ(NASA๋กœ๋ถ€ํ„ฐ ์กฐ๋งŒ๊ฐ„ ์ „ํ™”๋ฅผ ๋ฐ›์„ ๊ฒƒ ๊ฐ™์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค), ์—ฌ๊ธฐ์„œ ๋ถ„์„ํ•  ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ SpaceStation ํด๋ž˜์Šค์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์ฑ…์ž„(๋˜๋Š” ์ž‘์—…)์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋‘ ์œ ํ˜•์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„ผ์„œ;
  • ์†Œ๋ชจํ’ˆ(์†Œ๋ชจํ’ˆ);
  • ์—ฐ๋ฃŒ;
  • ๊ฐ€์†๊ธฐ.

์—ญ ์ง์› ์ค‘ ์–ด๋Š ๋ˆ„๊ตฌ๋„ ์ˆ˜์—…์„ ๋ฐฐ์ •๋ฐ›์ง€ ๋ชปํ•˜๋”๋ผ๋„ ๋ˆ„๊ฐ€ ๋ฌด์—‡์„ ๋‹ด๋‹นํ•˜๋Š”์ง€ ์‰ฝ๊ฒŒ ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๊ณผํ•™์ž๋Š” ์„ผ์„œ๋ฅผ ์ œ์–ดํ•˜๊ณ  ๋ฌผ๋ฅ˜ ์ „๋ฌธ๊ฐ€๋Š” ์ž์› ๊ณต๊ธ‰์„ ๋‹ด๋‹นํ•˜๋ฉฐ ์—”์ง€๋‹ˆ์–ด๋Š” ์—ฐ๋ฃŒ ๊ณต๊ธ‰์„ ๋‹ด๋‹นํ•˜๊ณ  ์กฐ์ข…์‚ฌ๋Š” ๋ถ€์Šคํ„ฐ๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.

์ด ํ”„๋กœ๊ทธ๋žจ์ด SRP๋ฅผ ์ค€์ˆ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋ฌผ๋ก ์ด์ง€. ํ•˜์ง€๋งŒ SpaceStation ํด๋ž˜์Šค๋Š” ๋ชจ๋“  ๊ฒƒ์„ ์•Œ๊ณ  ๋ชจ๋“  ์ผ์„ ํ•˜๋Š” ์ „ํ˜•์ ์ธ "์‹  ๊ฐœ์ฒด"์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์ฃผ์š” ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ดˆ๋ณด์ž์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๊ฐœ์ฒด๋Š” ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋žจ์€ ๋งค์šฐ ๊ฐ„๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚ ์ง€ ์ƒ์ƒํ•ด ๋ณด์„ธ์š”. ์•„๋งˆ๋„ ์šฐ๋ฆฌ ์šฐ์ฃผ ์ •๊ฑฐ์žฅ์—๋Š” ์˜๋ฃŒ ์Šคํ…Œ์ด์…˜์ด๋‚˜ ํšŒ์˜์‹ค์ด ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋Šฅ์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ์šฐ์ฃผ์ •๊ฑฐ์žฅ์€ ๋”์šฑ ์ปค์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ธ€์Ž„์š”, ์ด ์‹œ์„ค์€ ๋‹ค๋ฅธ ์‹œ์„ค๊ณผ ์—ฐ๊ฒฐ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ๋‹จ์ง€์— ๋Œ€ํ•œ ์„œ๋น„์Šค๋Š” ๋”์šฑ ๋ณต์žกํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ฐ€์†๊ธฐ ๋“ฑ์˜ ์ž‘๋™์„ ๋ฐฉํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฐ๊ตฌ์›์ด ์„ผ์„œ ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•˜๋ฉด ์ด๋Š” ์Šคํ…Œ์ด์…˜์˜ ํ†ต์‹  ์‹œ์Šคํ…œ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SRP ์›์น™์„ ์–ด๊ธฐ๋ฉด ๋‹จ๊ธฐ์ ์œผ๋กœ๋Š” ์ „์ˆ ์  ์Šน๋ฆฌ๋ฅผ ๊ฑฐ๋‘˜ ์ˆ˜ ์žˆ์„์ง€ ๋ชจ๋ฅด์ง€๋งŒ ๊ฒฐ๊ตญ์—๋Š” โ€œ์ „์Ÿ์—์„œ ํŒจํ•˜๊ฒŒโ€ ๋˜๊ณ  ์•ž์œผ๋กœ๋„ ์ด๋Ÿฐ ๊ดด๋ฌผ์„ ์œ ์ง€ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ค์›Œ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋žจ์„ ๊ฐ๊ฐ ํŠน์ • ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ณ„๋„์˜ ์ฝ”๋“œ ์„น์…˜์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ดํ•ดํ•˜๊ณ  SpaceStation ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฑ…์ž„์„ ๋ถ„์‚ฐ์‹œํ‚ค์ž

์œ„์—์„œ ์šฐ๋ฆฌ๋Š” SpaceStation ํด๋ž˜์Šค์— ์˜ํ•ด ์ œ์–ด๋˜๋Š” ๋„ค ๊ฐ€์ง€ ์œ ํ˜•์˜ ์ž‘์—…์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌํŒฉํ† ๋งํ•  ๋•Œ ์ด๋ฅผ ์—ผ๋‘์— ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. ์—…๋ฐ์ดํŠธ๋œ ์ฝ”๋“œ๋Š” SRP์™€ ๋” ์ž˜ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

class SpaceStation
  attr_reader :sensors, :supply_hold, :fuel_tank, :thrusters
 
  def initialize
    @supply_hold = SupplyHold.new
    @sensors = Sensors.new
    @fuel_tank = FuelTank.new
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
 
  def initialize
    @supplies = {}
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
end
 
class Thrusters
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @linked_fuel_tank.get_fuel_levels >= 10
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(10)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

๋งŽ์€ ๋ณ€ํ™”๊ฐ€ ์žˆ์–ด ์ด์ œ ํ”„๋กœ๊ทธ๋žจ์ด ํ™•์‹คํžˆ ๋” ์ข‹์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด์ œ SpaceStation ํด๋ž˜์Šค๋Š” ์„ผ์„œ ์„ธํŠธ, ์†Œ๋ชจํ’ˆ ๊ณต๊ธ‰ ์‹œ์Šคํ…œ, ์—ฐ๋ฃŒ ํƒฑํฌ ๋ฐ ๋ถ€์Šคํ„ฐ๋ฅผ ํฌํ•จํ•œ ์ข…์† ๋ถ€ํ’ˆ์— ๋Œ€ํ•œ ์ž‘์—…์ด ์‹œ์ž‘๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ์— ๋” ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ์ด์ œ ํ•ด๋‹น ํด๋ž˜์Šค์ธ ์„ผ์„œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณต๊ธ‰ ๋ณด๋ฅ˜; ์—ฐ๋ฃŒ ํƒฑํฌ; ์ถ”์ง„๊ธฐ.

์ด ๋ฒ„์ „์˜ ์ฝ”๋“œ์—๋Š” ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์š”์ ์€ ๊ฐœ๋ณ„ ํ•จ์ˆ˜๊ฐ€ ์ž์ฒด ํด๋ž˜์Šค์— ์บก์Šํ™”๋  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ณ  ์ผ๊ด€์„ฑ์„ ๊ฐ–๋„๋ก ๊ตฌ์„ฑ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ผ๊ด€์„ฑ์˜ ์›์น™์„ ๋”ฐ๋ฅด๊ธฐ ์œ„ํ•ด ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์š”์†Œ๋ฅผ ๊ทธ๋ฃนํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ํ•ด์‹œ ๊ตฌ์กฐ์—์„œ ๋ฐฐ์—ด๋กœ ์ด๋™ํ•˜์—ฌ ์‹œ์Šคํ…œ ์ž‘๋™ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ SupplyHold ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ๊ฑด๋“œ๋ฆด ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฌผ๋ฅ˜ ๋‹ด๋‹น์ž๊ฐ€ ์ž์‹ ์˜ ์„น์…˜์—์„œ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ณ€๊ฒฝํ•˜๋”๋ผ๋„ ์Šคํ…Œ์ด์…˜์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ SpaceStation ํด๋ž˜์Šค๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ธ์‹ํ•˜์ง€๋„ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

์šฐ์ฃผ ์ •๊ฑฐ์žฅ์—์„œ ์ผํ•˜๋Š” ์šฐ๋ฆฌ ์ง์›๋“ค์€ ํ•„์š”ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•„๋งˆ๋„ ๋งŒ์กฑํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ์—๋Š” SupplyHold ๋ฐ FuelTank ํด๋ž˜์Šค์— ํฌํ•จ๋œ report_supplies ๋ฐ report_fuel๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ตฌ๊ฐ€ ๋ณด๊ณ  ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜๋„๋ก ์š”์ฒญํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? SupplyHold์™€ FuelTank ํด๋ž˜์Šค๋ฅผ ๋ชจ๋‘ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฐ๋ฃŒ์™€ ์†Œ๋ชจํ’ˆ ๋ฐฐ์†ก ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ์•„๋งˆ๋„ ๋™์ผํ•œ ํด๋ž˜์Šค๋ฅผ ๋ชจ๋‘ ๋‹ค์‹œ ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ์ด๋ฏธ SRP ์›์น™์„ ์œ„๋ฐ˜ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

class SpaceStation
  attr_reader :sensors, :supply_hold, :supply_reporter,
              :fuel_tank, :fuel_reporter, :thrusters
 
  def initialize
    @sensors = Sensors.new
    @supply_hold = SupplyHold.new
    @supply_reporter = SupplyReporter.new(@supply_hold)
    @fuel_tank = FuelTank.new
    @fuel_reporter = FuelReporter.new(@fuel_tank)
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
  attr_reader :reporter
 
  def initialize
    @supplies = {}
  end
 
  def get_supplies
    @supplies
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
  attr_reader :reporter
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
end
 
class Thrusters
  FUEL_PER_THRUST = 10
 
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    
    if @linked_fuel_tank.get_fuel_levels >= FUEL_PER_THRUST
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(FUEL_PER_THRUST)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end
 
class Reporter
  def initialize(item, type)
    @linked_item = item
    @type = type
  end
 
  def report
    puts "----- #{@type.capitalize} Report -----"
  end
end
 
class FuelReporter < Reporter
  def initialize(item)
    super(item, "fuel")
  end
 
  def report
    super
    puts "#{@linked_item.get_fuel_levels} units of fuel available."
  end
end
 
class SupplyReporter < Reporter
  def initialize(item)
    super(item, "supply")
  end
 
  def report
    super
    if @linked_item.get_supplies.keys.length > 0
      @linked_item.get_supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
iss = SpaceStation.new
 
iss.sensors.run_sensors
  # ----- Sensor Action -----
  # Running sensors!
 
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Supply Error: Insufficient parts in the supply hold.
iss.supply_hold.load_supplies("parts", 10)
  # ----- Supply Action -----
  # Loading 10 units of parts in the supply hold.
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Using 2 of parts from the supply hold.
iss.supply_reporter.report
  # ----- Supply Report -----
  # parts avalilable: 8 units
 
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thruster Error: Insufficient fuel available.
iss.fuel_tank.load_fuel(100)
  # ----- Fuel Action -----
  # Loading 100 units of fuel in the tank.
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thrusting action successful.
  # ----- Fuel Action -----
  # Using 10 units of fuel from the tank.
iss.fuel_reporter.report
  # ----- Fuel Report -----
# 90 units of fuel available.

์ด ์ตœ์‹  ๋ฒ„์ „์˜ ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์ฑ…์ž„์ด FuelReporter์™€ SupplyReporter๋ผ๋Š” ๋‘ ๊ฐœ์˜ ์ƒˆ๋กœ์šด ํด๋ž˜์Šค๋กœ ๋‚˜๋ˆ„์–ด์กŒ์Šต๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค Reporter ํด๋ž˜์Šค์˜ ์ž์‹์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์›ํ•˜๋Š” ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก SpaceStation ํด๋ž˜์Šค์— ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ง€๊ตฌ๊ฐ€ ๋‹ค๋ฅธ ๊ฒƒ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜๋ฉด ๋ฉ”์ธ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ ํ•˜์œ„ ํด๋ž˜์Šค๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ์ผ๋ถ€ ์ˆ˜์—…์€ ์—ฌ์ „ํžˆ โ€‹โ€‹์„œ๋กœ ์˜์กดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ SupplyReporter ๊ฐœ์ฒด๋Š” SupplyHold์— ์ข…์†๋˜๊ณ  FuelReporter๋Š” FuelTank์— ์ข…์†๋ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋ถ€์Šคํ„ฐ๋Š” ์—ฐ๋ฃŒํƒฑํฌ์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์—์„œ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์ด๋ฏธ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ณด์ด๋ฉฐ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ํŠน๋ณ„ํžˆ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐœ์ฒด์˜ ์ฝ”๋“œ๋ฅผ ํŽธ์ง‘ํ•ด๋„ ๋‹ค๋ฅธ ๊ฐœ์ฒด์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ๊ฐ ๊ฐœ์ฒด/ํด๋ž˜์Šค์˜ ์ฑ…์ž„์ด ์ •ํ™•ํ•˜๊ฒŒ ์ •์˜๋œ ๋ชจ๋“ˆ์‹ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฝ”๋“œ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์€ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์œผ๋ฉฐ ์œ ์ง€ ๊ด€๋ฆฌ๋Š” ๊ฐ„๋‹จํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค. "์‹ ์„ฑํ•œ ๋ฌผ์ฒด" ์ „์ฒด๋ฅผ SRP๋กœ ๋ณ€ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€