옵저버 패턴 (이벤트)
Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 54번째.
옵저버 패턴(Observer pattern)은 하나의 객체(주체)의 상태가 변경될 때 여러 의존적인 객체들(옵저버들)에게 알림을 보내는 일대다 관계를 구축합니다. C#에서 이벤트는 이 패턴을 우아하게 구현할 수 있는 내장된 방식을 제공합니다.
이 패턴은 이벤트를 발생시키는 게시자(publisher)와 그에 반응하는 구독자(subscriber)를 포함합니다. 다음은 주가 모니터의 실용적인 예시입니다:
public class Stock
{
private decimal _price;
public event Action<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
_price = value;
PriceChanged?.Invoke(_price); // 모든 관찰자에게 알림
}
}
}
public class PriceAlert
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Alert: Price is now {newPrice}");
}
}
public class PriceLogger
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Logged: {newPrice}");
}
}구독자는 +=를 사용하여 메서드를 이벤트에 연결하고 -=를 사용하여 연결을 해제할 수 있습니다:
var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();
stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;
stock.Price = 150.50m;
// 출력:
// 알림: 현재 가격은 150.50입니다
// 로그 기록됨: 150.50
stock.PriceChanged -= logger.OnPriceChanged; // 구독 해제
stock.Price = 155.00m;
// 출력: 알림: 현재 가격은 155.00입니다옵저버 패턴은 느슨한 결합을 촉진합니다. Stock 클래스는 자신의 옵저버들에 대해 아무것도 알 필요가 없습니다. 새로운 옵저버들은 발행자(publisher)를 수정하지 않고도 구독할 수 있어, 시스템을 확장하고 유지 관리하기 쉽게 만듭니다.
챌린지
쉬움이벤트와 옵저버 패턴을 사용하여 온도 모니터링 시스템을 구축해 보겠습니다. 온도가 변할 때마다 여러 옵저버에게 알림을 보내는 센서를 만들 것입니다. 이는 서로 다른 구성 요소가 온도 업데이트에 반응해야 하는 기후 제어 시스템과 같은 시나리오에 적합합니다.
코드는 세 개의 파일로 구성됩니다:
TemperatureSensor.cs:Monitoring네임스페이스에TemperatureSensor클래스를 생성합니다. 이것은 게시자(publisher)이자 다른 객체들이 관찰할 대상(subject)입니다. 센서는 다음을 포함해야 합니다:- 현재 온도를 저장하는 프라이빗 필드
- 구독자에게 새로운 온도를 알리기 위해
Action<double>을 사용하는TemperatureChanged라는 이름의 이벤트 - 온도가 새로운 값으로 설정될 때마다 이벤트를 발생시키는
Temperature속성
Observers.cs: 온도 변화를 구독할 두 개의 옵저버 클래스를 동일한 네임스페이스에 생성합니다:DisplayUnit-Display: {temp}C를 출력하는OnTemperatureChanged(double temp)메서드를 가집니다.AlarmSystem-Alarm Check: {temp}C를 출력하는OnTemperatureChanged(double temp)메서드를 가집니다.
Program.cs: 센서와 두 옵저버를 생성하여 모든 것을 하나로 묶습니다. 두 옵저버를 센서의 이벤트에 구독시킨 다음 온도를 업데이트합니다. 그 후, 알람 시스템의 구독을 해제하고 온도를 다시 업데이트하여 디스플레이 유닛만 알림을 받는지 확인합니다.
두 개의 입력을 받게 됩니다:
- 첫 번째 온도 측정값 (예:
22.5) - 두 번째 온도 측정값 (예:
28.0)
프로그램은 다음을 수행해야 합니다:
- 센서와 두 옵저버를 생성합니다.
- 두 옵저버를
TemperatureChanged이벤트에 구독시킵니다. - 센서의 온도를 첫 번째 입력값으로 설정합니다 (두 옵저버 모두 알림을 받아야 합니다).
- 이벤트에서
AlarmSystem의 구독을 해제합니다. - 센서의 온도를 두 번째 입력값으로 설정합니다 (디스플레이만 알림을 받아야 합니다).
예를 들어, 입력값이 22.5와 28.0인 경우 출력은 다음과 같아야 합니다:
Display: 22.5C
Alarm Check: 22.5C
Display: 28C이것은 옵저버 패턴의 핵심적인 힘을 보여줍니다. 센서는 무엇이 자신을 듣고 있는지 알 필요도 없고 신경 쓰지도 않습니다. 옵저버는 동적으로 구독 및 해제할 수 있으며, 게시자는 단순히 현재 듣고 있는 대상에게 알림을 보낼 뿐입니다!
치트 시트
옵저버 패턴(Observer pattern)은 한 객체(주체)의 상태가 변경될 때 여러 의존 객체(옵저버)들에게 알림을 보내는 일대다 관계를 구축합니다.
C#에서 이벤트는 이 패턴을 구현하기 위한 내장된 방법을 제공합니다. 이 패턴은 이벤트를 발생시키는 게시자(publisher)와 그에 반응하는 구독자(subscribers)를 포함합니다.
기본 이벤트 구현
event Action<T>를 사용하여 이벤트를 정의하고 상태가 변경될 때 이를 호출합니다:
public class Stock
{
private decimal _price;
public event Action<decimal> PriceChanged;
public decimal Price
{
get => _price;
set
{
_price = value;
PriceChanged?.Invoke(_price); // 모든 옵저버에게 알림
}
}
}옵저버 생성하기
옵저버는 이벤트 시그니처와 일치하는 메서드를 가진 클래스들입니다:
public class PriceAlert
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Alert: Price is now {newPrice}");
}
}
public class PriceLogger
{
public void OnPriceChanged(decimal newPrice)
{
Console.WriteLine($"Logged: {newPrice}");
}
}구독 및 구독 해제
이벤트를 구독하려면 +=를 사용하고, 구독을 해제하려면 -=를 사용합니다:
var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();
stock.PriceChanged += alert.OnPriceChanged; // 구독
stock.PriceChanged += logger.OnPriceChanged; // 구독
stock.Price = 150.50m; // 두 옵저버 모두에게 알림이 전송됨
stock.PriceChanged -= logger.OnPriceChanged; // 구독 해제
stock.Price = 155.00m; // alert에게만 알림이 전송됨옵저버 패턴은 느슨한 결합(loose coupling)을 촉진합니다. 게시자는 옵저버에 대해 아무것도 알 필요가 없습니다. 게시자를 수정하지 않고도 새로운 옵저버가 구독할 수 있습니다.
직접 해보기
using System;
using Monitoring;
class Program
{
public static void Main(string[] args)
{
// 입력 온도 읽기
double temp1 = Convert.ToDouble(Console.ReadLine());
double temp2 = Convert.ToDouble(Console.ReadLine());
// TODO: TemperatureSensor 인스턴스 생성
// TODO: DisplayUnit 및 AlarmSystem 인스턴스 생성
// TODO: 두 옵저버를 TemperatureChanged 이벤트에 구독
// TODO: 센서의 온도를 temp1으로 설정 (두 옵저버 모두 알림을 받음)
// TODO: 이벤트에서 AlarmSystem 구독 해제
// TODO: 센서의 온도를 temp2로 설정 (디스플레이만 알림을 받음)
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.