virtual 메서드

인스턴스 메서드 선언에 virtual 한정자가 있는 경우 해당 메서드를 가상 메서드라고 한다. virtual 한정자가 없으면 비가상 메서드라고 한다. 따라서 평소에 쓰는 메서드들은 비가상 메서드들.

virtual 한정자는 static, abstract, private 또는 override 한정자와 함께 사용할 수 없다.

 

abstract와의 차이점

  1. 부모 클래스에서 함수를 정의해도 되고 안해도 된다.
  2. static 에는 virtual 한정자를 사용할 수 없다.
  3. 상속된 virtual은 override 한정자를 사용하는 속성선언을 포함하는 방법을 통해 자식 클래스에서 재정의 할 수 있다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

using System;

class A

{

   public void F() { Console.WriteLine("A.F"); }

   public virtual void G() { Console.WriteLine("A.G"); }

}

class B: A

{

   new public void F() { Console.WriteLine("B.F"); }

   public override void G() { Console.WriteLine("B.G"); }

}

class Test

{

   static void Main() {

      B b = new B();

      A a = b;

      a.F();

      b.F();

      a.G();

      b.G();

   }

    //output

    //A.F

    //B.F

    //B.G

    //B.G

}

cs

A는 비가상 메서드 F와 가상 메서드 G를 정의한다. 클래스 B는 새로운 비가상 메서드 F를 정의하므로 상속된 F를 숨기며 상속된 메서드 G도 재정의한다.

a.G() 문은 A.G가 아니라 B.G를 호출한다. 이는 인스턴스의 컴파일 타임 형식(A)이 아니라 인스턴스의 런타임 형식(B)이 호출할 실제 메서드 구현을 결정하기 때문이다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

using System;

class A

{

   public virtual void F() { Console.WriteLine("A.F"); }

}

class B: A

{

   public override void F() { Console.WriteLine("B.F"); }

}

class C: B

{

   new public virtual void F() { Console.WriteLine("C.F"); }

}

class D: C

{

   public override void F() { Console.WriteLine("D.F"); }

}

class Test

{

   static void Main() {

      D d = new D();

      A a = d;

      B b = d;

      C c = d;

      a.F();

      b.F();

      c.F();

      d.F();

   

    //output

    //B.F

    //B.F

    //D.F

    //D.F

   }

}

cs

C 클래스와 D 클래스에는 시그니처가 같은 두 개의 가상 메서드가 포함되어 있습니다. 하나는 A에 의해 정의된 것이고, 다른 하나는 C에 의해 정의된 것입니다. C에 의해 정의된 메서드는 A에서 상속된 메서드를 숨깁니다. 그러므로 D의 재정의 선언에서는 C에 의해 정의된 메서드를 재정의하지만, D가 A에 의해 정의된 메서드를 재정의할 수는 없습니다.

 

● override와 new의 차이

override 한정자는 기본 클래스 메서드를 확장하며, new 한정자는 그것을 숨깁니다.

기본 클래스 메서드는 virtual로 정의해야 합니다.

파생 클래스의 메서드 앞에 new 또는 override 키워드를 사용하지 않으면 컴파일러에서 경고가 발생하고 메서드에 new 키워드가 있는 것처럼 작동합니다.

override는 일반적으로 상속 계층 구조에서 원하는 동작입니다. 파생 클래스에서 정의되는 메서드를 사용하도록 파생 클래스에서 생성되는 값을 가진 개체를 사용합니다. override 를 사용하여 기본 클래스 메서드를 확장하는 동작을 달성합니다.

override를 하지 않고 new로 함수를 만든다면 표시되는 이름만 같을 뿐 다른 함수이다.

'C#' 카테고리의 다른 글

Decorator pattern(데코레이터 패턴)  (0) 2015.10.20
Protected Internal과 Internal의 차이  (0) 2015.09.17
Observer Pattern (옵저버 디자인 패턴)  (0) 2015.09.17
함수와 메소드의 차이  (0) 2015.09.16
C# Linq  (0) 2015.04.08

데코레이터 패턴 : 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브 클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법.

 

※ 장점

  1. 데코레이터의 수퍼클래스는 자신이 장식하고 있는 객체의 수퍼클래스와 같다.
  2. 한 객체를 여러 개의 데코레이터로 감쌀 수 있다.
  3. 데코레이터는 자신이 감싸고 있는 객체와 같은 수퍼클래스를 가지고 있기 때문에 원래 객체(싸여져 있는 객체)가 들어갈 자리에 데코레이터 객체를 넣어도 상관 없습니다.
  4. 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행할 수 있습니다.
  5. 객체는 언제든지 감쌀 수 있기 때문에 실행중에 필요한 데코레이터를 마음대로 적용할 수 있습니다.

 

※ 단점

  • 데코레이터 패턴을 이용해 디자인을 하다 보면 잡다한 클래스가 많아 질 수 있다.
  • 겹겹이 애워싼 객체의 정체를 알기가 힘들다.
  • 상속을 통해 확장할 수도 있지만, 디자인 유연성 면에서는 별로 좋지 않다.

 




  • Beverage.cs

1

2

3

4

5

6

7

8

9

10

11

public abstract class Beverage

{

    protected String description = "제목없음";

   

    public abstract double cost();

   

    public virtual string getDescription()

    {

        return description;

    }

}

 

 

Virtual : 가상 메소드

메소드 선언에 virtual 한정자가 있는 경우 해당 메소드를 가상 메소드라고 한다.

자식클래스에 의해서 재정의(override) 될 수 있다는 의미를 가지고 있다. 하지만 반드시 재정의 할 필요는 없다.

virtual 한정자는 static, abstract, private 또는 override 한정자와 함께 사용할 수 없다.

자식클래스에서 override를 하지 않는다면 컴파일러에서 경고가 발생하고 메소드에 new 키워드가 있는 것처럼 작동한다.

 

  • Decaf.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

public class Decaf : Beverage

{

    public Decaf()

    {

        description = "디카페인 커피";

    }

   

   

    public override double cost()

    {

        return 1.05;

    }

}

 

 

  • CondimentDecorator.cs

1

2

3

4

public abstract class CondimentDecorator : Beverage

{

    public override abstract string getDescription();

}

 

 

  • Milk.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class Milk: CondimentDecorator

 {

    private Beverage beverage;

   

    public Milk(Beverage beverage)

    {

        this.beverage = beverage;

    }

   

    public override string getDescription()

    {

        return beverage.getDescription() + ", 스팀밀크";

    }

   

    public override double cost()

    {

        return beverage.cost() + .10;

    }

 }

cs

 

  • Mocha.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class Mocha : CondimentDecorator

{

    private Beverage beverage;

   

    public Mocha(Beverage beverage)

    {

        this.beverage = beverage;

    }

   

    public override String getDescription()

    {

        return beverage.getDescription() + ", 모카";

    }

   

    public override double cost()

    {

        return beverage.cost() + .20;

    }

}

 

 

  • Program.cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

 

static void Main(string[] args)

{

    // 에스프레소 커피

        Beverage espresso = new Espresso();

       Console.WriteLine(espresso.getDescription() + " : $" + espresso.cost());

   

        // 다크로스트 커피 + 모카 + 모카 + 휘핑 크림

        Beverage darkRoast = new DarkRoast();

        darkRoast = new Mocha(darkRoast);       // 모카 추가

        darkRoast = new Mocha(darkRoast);       // 모카 한번 더 추가

        darkRoast = new Whip(darkRoast);        // 휘핑 크림 추가

       Console.WriteLine(darkRoast.getDescription() + " : $" + darkRoast.cost());

   

        // 하우스블렌드 커피 + 두유 + 모카 + 휘핑크림

        Beverage houseBlend = new HouseBlend();

        houseBlend = new Soy(houseBlend);

        houseBlend = new Mocha(houseBlend);

        houseBlend = new Whip(houseBlend);

        Console.WriteLine(houseBlend.getDescription() + " : $" + houseBlend.cost());

}

cs

 

 

 

'C#' 카테고리의 다른 글

virtual, override, new 메서드  (0) 2015.10.20
Protected Internal과 Internal의 차이  (0) 2015.09.17
Observer Pattern (옵저버 디자인 패턴)  (0) 2015.09.17
함수와 메소드의 차이  (0) 2015.09.16
C# Linq  (0) 2015.04.08
  • protected : 포함하는 클래스 또는 여기에서 파생된 형식으로 액세스가 제한됩니다.
  • internal : 현재 어셈블리 또는 포함하는 클래스에서 파생된 형식으로 액세스가 제한됩니다.
  • protected internal : 현재 어셈블리 또는 포함하는 클래스에서 파생된 형식으로 액세스가 제한됩니다.

protected internal 조합을 사용할 때를 제외하고는 멤버 또는 형식 하나에 액세스 한정자를 한 개만 지정할 수 있습니다.

 

internal

어셈블리는 한 프로젝트가 뽑아내는 결과물.

이 결과물 내에서는 public 처럼 접근이 가능한데 다른 프로젝트에서 참조하여 사용하고자 할 때는 어셈블리가 다르기 때문에 internal 클래스에 접근 할 수 없다.

 

protected internal

protected intenal은 protected && internal이 아니라 protected || internal 그래서 외부 프로젝트에서 접근 할 수 있음.

internal이 선언된 클래스는 외부 프로젝트에서 접근 할 수 없기 때문에 외부 프로젝트에서 dll을 참조하여 상속 받는 경우에 사용

'C#' 카테고리의 다른 글

virtual, override, new 메서드  (0) 2015.10.20
Decorator pattern(데코레이터 패턴)  (0) 2015.10.20
Observer Pattern (옵저버 디자인 패턴)  (0) 2015.09.17
함수와 메소드의 차이  (0) 2015.09.16
C# Linq  (0) 2015.04.08

C#에서 옵저버 패턴 사용하기

 

 

 

Observer Pattern

객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 1:N 의존성을 정의합니다.

푸시 기반 알림이 필요한 곳에 사용

신문사 + 독자 = 옵저버패턴

 

의존성: 데이터의 주인은 주제이다. 옵저버는 데이터가 변경되었을 때 주제에서 갱신해 주기를 기다린다.

1:N: 상태를 저장하고 지배하는 것은 주제 객체이다.

 

디자인 원칙: 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.

 

느슨한 결합(Loose Coupling)

  • 주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스(Observer Interface)를 구현한다는 것 뿐이다.
  • 옵저버는 언제든지 새로 추가 할 수 있다.
  • 새로운 형식의 옵저버를 추가하려고 할 때도 주제를 전혀 변경할 필요가 없다.
  • 주제와 옵저버는 서로 독립적으로 재 사용할 수 있다.
  • 주제나 옵저버가 바뀌더라도 서로에게 영향을 미치지 않는다.

 

패턴 적용

  • 옵저버에게 알림을 전송하는 개체인 주제객체 또는 주제객체. 주제객체는 IObservable<T> 인터페이스를 구현하는 클래스 또는 구조체입니다. 주제객체는 주제객체로부터 알림을 수신하려는 옵저버가 호출하는 단일 메서드IObservable<T>.Subscribe를 구현해야 합니다.
  • 주제객체로부터 알림을 수신하는 개체인 옵저버. 옵저버는 IObserver<T> 인터페이스를 구현하는 클래스 또는 구조체입니다. 옵저버는 모두 주제객체에 의해 호출되는 다음 세 개의 메서드를 구현해야 합니다
    • IObserver<T>.OnNext - 옵저버에게 새 정보나 현재 정보를 제공합니다.
    • IObserver<T>.OnError - 오류가 발생했음을 옵저버에게 알립니다.
    • IObserver<T>.OnCompleted - 주제객체가 알림 전송을 완료했음을 나타냅니다.
  • 일반적으로 옵저버는 System.Collections.Generic.List<T> 개체와 같은 컨테이너 개체를 사용하여 알림을 구독한 IObserver<T> 구현에 대한 참조를 보유합니다. 이 목적으로 저장소 컨테이너를 사용하면 주제객체가 0개에서 무한대 개수까지 옵저버를 처리할 수 있습니다. 옵저버가 알림을 수신하는 순서는 정의되지 않습니다. 주제객체가 임의 메서드를 사용하여 순서를 결정할 수 있습니다.
  • 알림이 완료될 때 주제객체가 옵저버를 제거할 수 있도록 하는 IDisposable 구현. 옵저버는 Subscribe 메서드로부터 IDisposable 구현에 대한 참조를 수신하므로 주제객체가 알림 전송을 완료하기 전에 IDisposable.Dispose 메서드를 호출하여 구독을 취소할 수도 있습니다.
  • 주제객체가 해당 옵저버에게 전송하는 데이터를 포함하는 개체. 이 개체의 형식은 IObservable<T> 및 IObserver<T> 인터페이스의 제네릭 형식 매개 변수에 해당합니다. 이 개체는 IObservable<T> 구현과 동일할 수도 있지만 일반적으로 별도 형식입니다.

패턴 구현

객체

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

using System;

using System.Collections.Generic;

   

public class BaggageInfo

{

   private int flightNo;

   private string origin;

   private int location;

   

   internal BaggageInfo(int flight, string from, int carousel)

   {

      this.flightNo = flight;

      this.origin = from;

      this.location = carousel;

   }

   

   public int FlightNumber {

      get { return this.flightNo; }

   }

   

   public string From {

      get { return this.origin; }

   }

   

   public int Carousel {

      get { return this.location; }

   }

}

cs

 

BaggageInfo 클래스는 도착 항공편과 각 항공편의 수하물을 찾을 수 있는 컨베이어 벨트에 대한 정보를 제공합니다.

 

주제객체

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

public class BaggageHandler : IObservable<BaggageInfo>

{

   private List<IObserver<BaggageInfo>> observers;

   private List<BaggageInfo> flights;

   

   public BaggageHandler()

   {

      observers = new List<IObserver<BaggageInfo>>();

      flights = new List<BaggageInfo>();

   }

   

   public IDisposable Subscribe(IObserver<BaggageInfo> observer)

   {

      // Check whether observer is already registered. If not, add it

      if (! observers.Contains(observer)) {

         observers.Add(observer);

         // Provide observer with existing data.

         foreach (var item in flights)

            observer.OnNext(item);

      }

      return new Unsubscriber<BaggageInfo>(observers, observer);

   }

   

   // Called to indicate all baggage is now unloaded.

   public void BaggageStatus(int flightNo)

   {

      BaggageStatus(flightNo, String.Empty, 0);

   }

   

   public void BaggageStatus(int flightNo, string from, int carousel)

   {

      var info = new BaggageInfo(flightNo, from, carousel);

   

      // Carousel is assigned, so add new info object to list.

      if (carousel > 0 && ! flights.Contains(info)) {

         flights.Add(info);

         foreach (var observer in observers)

            observer.OnNext(info);

      }

      else if (carousel == 0) {

         // Baggage claim for flight is done

         var flightsToRemove = new List<BaggageInfo>();

         foreach (var flight in flights) {

            if (info.FlightNumber == flight.FlightNumber) {

               flightsToRemove.Add(flight);

               foreach (var observer in observers)

                  observer.OnNext(info);

            }

         }

         foreach (var flightToRemove in flightsToRemove)

            flights.Remove(flightToRemove);

   

         flightsToRemove.Clear();

      }

   }

   

   public void LastBaggageClaimed()

   {

      foreach (var observer in observers)

         observer.OnCompleted();

   

      observers.Clear();

   }

}

Colored by Color Scripter

cs

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

 

internal class Unsubscriber<BaggageInfo> : IDisposable

{

   private List<IObserver<BaggageInfo>> _observers;

   private IObserver<BaggageInfo> _observer;

   

   internal Unsubscriber(List<IObserver<BaggageInfo>> observers, IObserver<BaggageInfo> observer)

   {

      this._observers = observers;

      this._observer = observer;

   }

   

   public void Dispose() 

   {

      if (_observers.Contains(_observer))

         _observers.Remove(_observer);

   }

}

cs

 

BaggageHandler 클래스는 도착 항공편 및 수하물을 찾을 수 있는 컨베이어 벨트에 대한 정보를 받아야 합니다. (주제 객체)

내부적으로 다음 두 개의 컬렉션을 유지 관리합니다.

  • observers - 업데이트된 정보를 수신할 클라이언트 컬렉션입니다.
  • flights - 항공편 및 할당된 컨베이어 벨트 컬렉션입니다.

업데이트된 정보를 수신하려는 옵저버는 BaggageHandler.Subscribe 메서드를 호출합니다. 옵저버가 이전에 알림을 구독하지 않은 경우 클라이언트의 IObserver<T> 구현에 대한 참조가 observers 컬렉션에 추가됩니다.

오버로드된 BaggageHandler.BaggageStatus 메서드를 호출하여 항공편의 수하물을 내리는 중인지 여부를 나타낼 수 있습니다.

내리는 중이면 메서드에 항공편 번호, 출발 공항 및 수하물을 내리는 중인 컨베이어 벨트가 전달됩니다.

더 이상 내리지 않는 경우 메서드에 항공편 번호만 전달됩니다.

수하물을 내리는 경우 메서드는 메서드에 전달된 BaggageInfo 정보가 flights 컬렉션에 있는지 여부를 확인합니다. 그러지 않은 경우 메서드는 정보를 추가하고 각 옵저버의 OnNext 메서드를 호출합니다.

더 이상 수하물을 내리지 않는 항공편의 경우 메서드는 항공편에 대한 정보가 flights 컬렉션에 저장되었는지 여부를 확인합니다.

저장된 경우 메서드는 각 옵저버의 OnNext 메서드를 호출하고 flights 컬렉션에서 BaggageInfo 개체를 제거합니다.

그날의 마지막 항공편이 착륙하고 해당 수하물이 처리되면 BaggageHandler.LastBaggageClaimed 메서드가 호출됩니다.

이 메서드는 각 옵저버의 OnCompleted 메서드를 호출하여 모든 알림이 완료되었음을 나타내고 observers 컬렉션을 지웁니다.

주제객체의 Subscribe 메서드는 OnCompleted 메서드가 호출되기 전에 옵저버가 알림 수신을 중지할 수 있도록 하는 IDisposable 구현을 반환합니다.

BaggageHandler.Subscribe 메서드에서 클래스가 인스턴스화되면 observers 컬렉션에 대한 참조 및 컬렉션에 추가된 옵저버에 대한 참조가 전달됩니다.

이러한 참조는 지역 변수에 할당됩니다. 개체의 Dispose 메서드가 호출되면 옵저버가 observers 컬렉션에 여전히 있는지 여부를 확인하고, 있을 경우 옵저버를 제거합니다.

 

옵저버

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79 

using System;

using System.Collections.Generic;

   

public class ArrivalsMonitor : IObserver<BaggageInfo>

{

   private string name;

   private List<string> flightInfos = new List<string>();

   private IDisposable cancellation;

   private string fmt = "{0,-20} {1,5}  {2, 3}";

   

   public ArrivalsMonitor(string name)

   {

      if (String.IsNullOrEmpty(name))

         throw new ArgumentNullException("The observer must be assigned a name.");

   

      this.name = name;

   }

   

   public virtual void Subscribe(BaggageHandler provider)

   {

      cancellation = provider.Subscribe(this);

   }

   

   public virtual void Unsubscribe()

   {

      cancellation.Dispose();

      flightInfos.Clear();

   }

   

   public virtual void OnCompleted() 

   {

      flightInfos.Clear();

   }

   

   // No implementation needed: Method is not called by the BaggageHandler class.

   public virtual void OnError(Exception e)

   {

      // No implementation.

   }

   

   // Update information.

   public virtual void OnNext(BaggageInfo info) 

   {

      bool updated = false;

   

      // Flight has unloaded its baggage; remove from the monitor.

      if (info.Carousel == 0) {

         var flightsToRemove = new List<string>();

         string flightNo = String.Format("{0,5}", info.FlightNumber);

   

         foreach (var flightInfo in flightInfos) {

            if (flightInfo.Substring(215).Equals(flightNo)) {

               flightsToRemove.Add(flightInfo);

               updated = true;

            }

         }

         foreach (var flightToRemove in flightsToRemove)

            flightInfos.Remove(flightToRemove);

   

         flightsToRemove.Clear();

      }

      else {

         // Add flight if it does not exist in the collection.

         string flightInfo = String.Format(fmt, info.From, info.FlightNumber, info.Carousel);

         if (! flightInfos.Contains(flightInfo)) {

            flightInfos.Add(flightInfo);

            updated = true;

         }

      }

      if (updated) {

         flightInfos.Sort();

         Console.WriteLine("Arrivals information from {0}"this.name);

         foreach (var flightInfo in flightInfos)

            Console.WriteLine(flightInfo);

   

         Console.WriteLine();

      }

   }

}

 

수하물 찾는 곳 정보를 표시하는 기본 클래스인 ArrivalsMonitor라는 IObserver<T> 구현을 제공합니다.

ArrivalsMonitor 클래스에는 Subscribe 및 Unsubscribe 메서드가 포함됩니다.

Subscribe 메서드를 통해 클래스는 Subscribe 호출에서 반환된 IDisposable 구현을 전용 변수에 저장할 수 있습니다.

Unsubscribe 메서드를 통해 클래스는 주제객체의 Dispose 구현을 호출하여 알림 구독을 취소할 수 있습니다.

ArrivalsMonitor에서는 OnNext, OnError 및 OnCompleted 메서드의 구현도 제공합니다.

OnNext 구현에만 상당한 양의 코드가 포함됩니다. 메서드는 도착 항공편의 출발 공항 및 수하물을 찾을 수 있는 컨베이어 벨트에 대한 정보를 유지 관리하는 private, sorted, generic List<T> 개체로 작동합니다.

BaggageHandler 클래스가 새 항공편 도착을 보고하면 OnNext 메서드 구현에서 해당 항공편에 대한 정보를 목록에 추가합니다.

BaggageHandler 클래스가 항공편의 수하물을 내렸다고 보고하면 OnNext 메서드가 목록에서 해당 항공편을 제거합니다. 변경될 때마다 목록이 정렬되고 콘솔에 표시됩니다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

using System;

using System.Collections.Generic;

   

public class Example

{

   public static void Main()

   {

      BaggageHandler provider = new BaggageHandler();

      ArrivalsMonitor observer1 = new ArrivalsMonitor("BaggageClaimMonitor1");

      ArrivalsMonitor observer2 = new ArrivalsMonitor("SecurityExit");

   

      provider.BaggageStatus(712"Detroit"3);

      observer1.Subscribe(provider);

      provider.BaggageStatus(712"Kalamazoo"3);

      provider.BaggageStatus(400"New York-Kennedy"1);

      provider.BaggageStatus(712"Detroit"3);

      observer2.Subscribe(provider);

      provider.BaggageStatus(511"San Francisco"2);

      provider.BaggageStatus(712);

      observer2.Unsubscribe();

      provider.BaggageStatus(400);

      provider.LastBaggageClaimed();

   }

}

// The example displays the following output:

//      Arrivals information from BaggageClaimMonitor1

//      Detroit                712    3

//

//      Arrivals information from BaggageClaimMonitor1

//      Detroit                712    3

//      Kalamazoo              712    3

//

//      Arrivals information from BaggageClaimMonitor1

//      Detroit                712    3

//      Kalamazoo              712    3

//      New York-Kennedy       400    1

//

//      Arrivals information from SecurityExit

//      Detroit                712    3

//

//      Arrivals information from SecurityExit

//      Detroit                712    3

//      Kalamazoo              712    3

//

//      Arrivals information from SecurityExit

//      Detroit                712    3

//      Kalamazoo              712    3

//      New York-Kennedy       400    1

//

//      Arrivals information from BaggageClaimMonitor1

//      Detroit                712    3

//      Kalamazoo              712    3

//      New York-Kennedy       400    1

//      San Francisco          511    2

//

//      Arrivals information from SecurityExit

//      Detroit                712    3

//      Kalamazoo              712    3

//      New York-Kennedy       400    1

//      San Francisco          511    2

//

//      Arrivals information from BaggageClaimMonitor1

//      New York-Kennedy       400    1

//      San Francisco          511    2

//

//      Arrivals information from SecurityExit

//      New York-Kennedy       400    1

//      San Francisco          511    2

//

//      Arrivals information from BaggageClaimMonitor1

//      San Francisco          511    2

 

 

옵저버 패턴 사용시 주의점

  1. 스레딩
    1. 일반적으로 주제객체는 컬렉션 개체로 표시되는 옵저버 목록에 특정 옵저버를 추가하여 IObservable<T>.Subscribe 메서드를 구현하며, 옵저버 목록에서 특정 옵저버를 제거하여 IDisposable.Dispose 메서드를 구현합니다. 옵저버는 언제든지 이러한 메서드를 호출할 수 있습니다. 또한 주제객체/옵저버 계약에서는 IObserver<T>.OnCompleted 콜백 메서드 후 구독 취소 담당자를 지정하지 않으므로 주제객체와 옵저버가 모두 목록에서 같은 멤버를 제거하려고 할 수 있습니다. 이러한 가능성 때문에 Subscribe 및 Dispose 메서드는 모두 스레드로부터 안전해야 합니다. 일반적으로는 이를 위해 동시 컬렉션 또는 잠금을 사용합니다. 스레드로부터 안전하지 않은 구현은 스레드로부터 안전하지 않음을 명시적으로 문서화해야 합니다.
  2. 예외 처리
    1. 데이터 주제객체와 옵저버는 느슨하게 결합되므로 옵저버 디자인 패턴의 예외는 정보 제공용으로 사용됩니다. 이는 주제객체와 옵저버가 옵저버 디자인 패턴에서 예외를 처리하는 방식에 영향을 줍니다.
    2. 주제객체 - OnError 메서드 호출
      1. OnError 메서드는 IObserver<T>.OnNext 메서드와 같이 옵저버에 대한 정보 메시지로 사용됩니다. 그러나 OnNext메서드는 현재 또는 업데이트된 데이터를 옵저버에게 제공하는 반면 OnError 메서드는 주제객체가 유효한 데이터를 제공할 수 없음을 나타냅니다.
      2. 주제객체는 특정 요구 사항이 있는 경우 자체 예외를 처리해야 합니다.
      3. 주제객체는 옵저버가 특정 방식으로 예외를 처리한다고 예상하거나 처리하도록 요구해서는 안 됩니다.
      4. 주제객체는 업데이트 제공 기능을 손상시키는 예외를 처리할 때 OnError 메서드를 호출해야 합니다. 이러한 예외에 대한 정보를 옵저버에게 전달할 수 있습니다. 다른 경우에는 옵저버에게 예외에 대해 알릴 필요가 없습니다.
      5. 주제객체가 OnError 또는 IObserver<T>.OnCompleted 메서드를 호출한 후에는 추가 알림이 표시되지 않아야 하며 주제객체는 해당 옵저버의 구독을 취소할 수 있습니다. 그러나 옵저버는 OnError 또는 IObserver<T>.OnCompleted 알림을 받기 전이나 받은 후를 포함하여 언제든지 직접 구독을 취소할 수 있습니다. 옵저버 디자인 패턴에서는 구독 취소 담당자(주제객체 또는 옵저버)가 지정되지 않으므로 주제객체와 옵저버가 모두 구독 취소를 시도할 수 있습니다. 일반적으로 옵저버는 구독 취소 시 옵저버 컬렉션에서 제거됩니다. 단일 스레드 응용 프로그램에서는 제거를 시도하기 전에 IDisposable.Dispose 구현에서 개체 참조가 유효하며 개체가 옵저버 컬렉션의 멤버임을 확인해야 합니다. 다중 스레드 응용 프로그램에서는 System.Collections.Concurrent.BlockingCollection<T> 개체와 같은 스레드로부터 안전한 컬렉션 개체를 사용해야 합니다.
    3. 옵저버 - OnError 메서드를 구현
      1. 옵저버는 주제객체로부터 오류 알림을 받으면 예외를 정보로 처리해야 하며 특정 작업을 수행하지 않아도 됩니다.
      2. 옵저버는 주제객체로의 OnError 메서드 호출에 응답할 때 다음 모범 사례를 따라야 합니다.
      3. 옵저버는 OnNext 또는 OnError 등의 인터페이스 구현에서 예외를 throw해서는 안 됩니다. 옵저버가 예외를 throw하는 경우 해당 예외는 처리되지 않습니다.
      4. 호출 스택을 유지하려면 OnError 메서드로 전달된 Exception 개체를 throw하려는 옵저버는 예외를 throw하기 전에 래핑해야 합니다. 이렇게 하려면 표준 예외 개체를 사용해야 합니다.
  3. 그 외
    1. IObservable<T>.Subscribe 메서드에서 등록 취소를 시도하면 null 참조가 생성될 수 있으므로 이러한 방식은 사용하지 않는 것이 좋습니다.
    2. 옵저버 하나를 여러 주제객체에 연결할 수는 있지만, IObserver<T> 인스턴스를 IObservable<T> 인스턴스 하나에만 연결하는 패턴을 사용하는 것이 좋습니다.

 

 

 

참고 자료

  1. 공급자 구현 : https://msdn.microsoft.com/ko-kr/library/ff506345(v=vs.110).aspx
  2. 관찰자 구현 : https://msdn.microsoft.com/ko-kr/library/ff506346(v=vs.110).aspx
  3. 관찰자 디자인 패턴 유용한 정보 : https://msdn.microsoft.com/ko-kr/library/ff519622(v=vs.110).aspx
  4. 관찰자 디자인 패턴 : https://msdn.microsoft.com/ko-kr/library/ee850490(v=vs.110).aspx
  5. IDisposable 인터페이스 : https://msdn.microsoft.com/ko-kr/library/system.idisposable(v=vs.110).aspx
  6. String.Substring 메서드 (Int32, Int32) : https://msdn.microsoft.com/ko-kr/library/aka44szs(v=vs.110).aspx

'C#' 카테고리의 다른 글

virtual, override, new 메서드  (0) 2015.10.20
Decorator pattern(데코레이터 패턴)  (0) 2015.10.20
Protected Internal과 Internal의 차이  (0) 2015.09.17
함수와 메소드의 차이  (0) 2015.09.16
C# Linq  (0) 2015.04.08

http://blog.naver.com/cdincdin/30129625682


'C#' 카테고리의 다른 글

virtual, override, new 메서드  (0) 2015.10.20
Decorator pattern(데코레이터 패턴)  (0) 2015.10.20
Protected Internal과 Internal의 차이  (0) 2015.09.17
Observer Pattern (옵저버 디자인 패턴)  (0) 2015.09.17
C# Linq  (0) 2015.04.08

http://mrw0119.tistory.com/24

'C#' 카테고리의 다른 글

virtual, override, new 메서드  (0) 2015.10.20
Decorator pattern(데코레이터 패턴)  (0) 2015.10.20
Protected Internal과 Internal의 차이  (0) 2015.09.17
Observer Pattern (옵저버 디자인 패턴)  (0) 2015.09.17
함수와 메소드의 차이  (0) 2015.09.16

+ Recent posts