Menu
Coddy logo textTech

싱글턴 패턴

Coddy Ruby 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 42번째.

디자인 패턴은 일반적인 설계 문제에 대한 검증된 해결책입니다. 싱글턴 패턴은 클래스가 정확히 하나의 인스턴스를 가지며 어디서든 접근할 수 있도록 합니다. Ruby 표준 라이브러리에는 이 기능이 기본으로 포함되어 있습니다. require 'singleton'과 include Singleton은 클래스 메서드 instance를 추가합니다:

require 'singleton'

class Settings
  include Singleton
  attr_accessor :theme

  def initialize
    @theme = "light"
  end
end

Settings.instance.theme = "dark"
puts Settings.instance.theme
puts Settings.instance.equal?(Settings.instance)

출력:

dark
true

include Singleton은 new도 private으로 만들기 때문에 두 번째 인스턴스를 생성할 수 없습니다. instance를 요청하는 프로그램의 모든 부분은 동일한 객체를 가져옵니다:

require 'singleton'

class Counter
  include Singleton
  attr_reader :hits

  def initialize
    @hits = 0
  end

  def hit
    @hits += 1
  end
end

def page_a
  Counter.instance.hit
end

def page_b
  2.times { Counter.instance.hit }
end

page_a
page_b
puts Counter.instance.hits
begin
  Counter.new
rescue NoMethodError => e
  puts e.message
end

출력:

3
private method `new' called for class Counter

같은 아이디어를 직접 작성하면, 처음 사용할 때 인스턴스를 생성하는 클래스 메서드와 private new가 됩니다. 다음은 모듈이 대신해 주는 작업을 보여 줍니다:

class Config
  def self.instance
    @instance ||= new
  end
  private_class_method :new

  attr_accessor :level

  def initialize
    @level = 1
  end
end

Config.instance.level = 3
puts Config.instance.level

출력:

3

싱글턴은 신중하게 사용하세요. 싱글턴은 전역 상태입니다. 어떤 코드든 이를 변경할 수 있고, 테스트는 깨끗한 복사본에서 시작할 수 없습니다. 싱글턴은 로거 또는 구성처럼 프로그램마다 실제로 하나만 존재하는 것에 적합합니다. 객체를 매개변수로 전달하는 편이 더 명확한 경우가 많습니다:

# 테스트하기 더 쉬움: 로거가 전달됨
def process(order, log)
  log.write("processing #{order.id}")
end
challenge icon

챌린지

쉬움

include Singleton을 사용하여 싱글턴 AppLogger를 완성하세요. 이 클래스는 항목 배열을 유지하며, [INFO] message 또는 [ERROR] message(대문자로 표시된 레벨)를 저장하는 log(message, level), entries, error_count를 가집니다. functions.rb의 헬퍼 handle_info(message)와 handle_error(message)는 모두 AppLogger.instance를 통해 로그를 기록하므로 동일한 객체에 도달합니다.

제공된 코드는 info,message 또는 error,message 형식의 줄을 읽고, 이를 헬퍼를 통해 전달하며, 모든 항목과 errors: 1을 출력하고, AppLogger.new가 거부되는지 확인합니다.

코드는 app_logger.rb와 functions.rb에 작성하세요. main.rb에는 제공된 입력/출력 코드가 있으며 편집할 수 없습니다.

직접 해보기

require_relative 'app_logger'
require_relative 'functions'

# 제공된 입력/출력 코드: 그대로 유지하세요
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  level, message = line.split(",", 2)
  level == "error" ? handle_error(message) : handle_info(message)
end
AppLogger.instance.entries.each { |e| puts e }
puts "errors: #{AppLogger.instance.error_count}"
begin
  AppLogger.new
  puts "new is allowed"
rescue NoMethodError
  puts "new is private"
end
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Ruby 컴파일러