잊지 않겠습니다.

1의 경우에서 Strategy Pattern을 사용해서 각각 Marin, Firebet, Medic을 구현을 다 해놓고 나니, Zerg Unit를 다른 개발자가 개발해서 들고 왔다.
그런데, 각 unit의 method가 다르기 때문에 사용할 수가 객체가 어떤 형태인지에 따라 접근 방법을 달리 해줘야지 되는 문제가 발생되었다. 이럴 때에, Method의 Interface를 한번 덮어줘서 사용하고자 하는 interface로 변경해주는 것만으로, 이런 문제를 해결 할 수 가 있다. 이것이 Adapter Pattern이다. 

기본적으로 Zerg Unit는 MoveTo를 갖지만, Attack이 아닌 Bite Method를 갖는다. 
그렇지만, 본질적으로 Bite, RunTo Method는 BaseUnit의 Attack, MoveTo와 유사 동작을 하기 때문에
Public으로 선언된 각 Method를 변경시켜주면 된다. 

public abstract class ZergUnitBase
{
    //Terran 의 Unit과는 다른 Bite와 Run Method를 가지고 있다. 
    public abstract String Bite();
    public abstract String Run(String direction);
}

따라서, BaseUnit과의 호환성을 맞추어주기 위해서 ZergUnitToBaseUnit이라는 Adapter를 구성해준다.

public class ZergUnitToBaseUnit : BaseUnit
{
    private readonly ZergUnitBase zergUnitBase;

    public ZergUnitToBaseUnit(ZergUnitBase zergUnitBase)
    {
        this.zergUnitBase = zergUnitBase;
    }

    public override string Attack()
    {
        return zergUnitBase.Bite();
    }

    public override string MoveTo(string direction)
    {
        return zergUnitBase.Run(direction);
    }
}

구성된 zerg unit을 이용해서 terran과 zerg의 난타전을 테스트하면 다음과 같다.

BaseUnit unit1 = new ZergUnitToBaseUnit(new Zergling());
BaseUnit unit2 = new ZergUnitToBaseUnit(new Hydralisk());

Console.WriteLine(unit1.Attack());
Console.WriteLine(unit2.MoveTo("Terran"));
보면, Zerg와 Terran의 Unit을 다루는 법이 동일하게 변경되었다. 다른 인터페이스와 동일하나, class의 변경은 이루어지지 않는다.

Posted by Y2K
,
전에 한번 정리한 소스를 한번 정리해보는 내용으로...

Marin과 Medic, FIrebet.. 이 3개의 unit가 존재를 하고, 이 unit은 Attack과 Move라는 기본 동작을 갖는다.
Move라는 동작은 모든 Unit이 동일한 동작을 할 수 있지만, Attack 방법은 각기 다르고, 기본적으로 Attack이라는 Method를
갖지만 내부 구현이 다른 동작이 되게 된다. 

따라서, 각기 다른 동작을 구현하는 Class를 따로 뽑아내서 구현하는 Strategy Pattern이 사용 가능하다. 

먼저, 각기 다른 Attack에 대한 동작을 구현하는 Rifle, Firebazuka, Heal Class를 구현한다.

class Rifle : IAttack
{
    #region IAttack Members

    public string Attack()
    {
        return "dddd...";
    }

    #endregion
}

public class Firebazuka : IAttack
{
    #region IAttack Members

    public string Attack()
    {
        return "Fire!!...............";
    }

    #endregion
}

public class Heal : IAttack
{
    #region IAttack Members

    public string Attack()
    {
        return "Heal... ";
    }

    #endregion
}
그리고, 이 Attack Method의 IAttack을 인자로 갖는 BaseUnit class를 만들어준다. 
BaseUnit은 각 Unit의 parent class가 된다. 

class Runner : IMove
{
    #region IMove Members

    public string MoveTo(string direction)
    {
        return String.Format("To :{0}, Move it!", direction);
    }

    #endregion
}

public abstract class BaseUnit
{
    protected IAttack attackMethod;
    protected IMove moveMethod;

    protected BaseUnit()
    {
        
    }

    protected BaseUnit(IAttack attackMethod, IMove moveMethod)
    {
        this.attackMethod = attackMethod;
        this.moveMethod = moveMethod;
    }

    public virtual String Attack()
    {
        return attackMethod.Attack();
    }

    public virtual String MoveTo(String direction)
    {
        return moveMethod.MoveTo(direction);
    }
}
이렇게 구성된 BaseUnit을 이용해서 Marin, Firebet, Medic을 구현해준다. 구현된 코드는 다음과 같다.


public class Marin : BaseUnit
{
    public Marin() : base(new Rifle(), new Runner())
    {
        
    }
}
public class Firebet : BaseUnit
{
    public Firebet() : base(new Firebazuka(), new Runner())
    {
        
    }
}
public class Medic : BaseUnit
{
    public Medic() : base(new Heal(), new Runner())
    {
        
    }
}
구현된 Class를 이용해서 Test Code를 작성하면 다음과 같다. 

//Marin, Medic, Firebet의 class 생성
BaseUnit unit1 = new Marin();
BaseUnit unit2 = new Marin();
BaseUnit unit3 = new Medic();
BaseUnit unit4 = new Firebet();

//BaseUnit으로 Attack, MoveTo를 모두 구현
Console.WriteLine(unit1.Attack());
Console.WriteLine(unit2.Attack());
Console.WriteLine(unit3.Attack());
Console.WriteLine(unit4.Attack());

Console.WriteLine(unit1.MoveTo("Direction"));
Console.WriteLine(unit2.MoveTo("Direction"));
Console.WriteLine(unit3.MoveTo("Direction"));
Console.WriteLine(unit4.MoveTo("Direction"));
Strategy Pattern은 같은 Interface에서 각각 다른 동작을 하는 class들을 구현하는 방법으로, 
Method의 주체가 되는 Rifle, Firebazuk, Heal class의 구성이 관건이다. 다른 것을 객체화하라. 라는 
Design pattern의 성경같은 구절이 생각난다. 
Posted by Y2K
,
Kent Beck의 두개의 모자.
개발하기 위해 리팩토링을 사용할 때, 두가지 구별된 작업(기능 추가와 리팩토링)을 위해 시간을 나눠야지 된다. 기능을 추가할 때는 기존 코드를 건드려서는 안되고 단지 새로운 기능을 추가해야지 된다. 테스트를 추가하고, 테스트가 잘 동작하는지를 확인함으로써 진행상황을 알수 있다. 리팩토링을 할 때는 기능을 추가해서는 안되고, 단지 코드의 구조에만 신경 써야 한다. 그리고 어떤 테스트도 추가하지 않는다. 단지 인터페이스가 변하여 작업을 계속하기 위해서 어쩔 수 없는 경우에만 테스트를 수정 한다. 작업을 할때.. 자신이 어떤 모자를 쓰고 있는지를 알고 있어야지 된다. 


가장 어려운 이야기인듯.; 
기능을 추가하다보면, 이 기능이 추가될때, 구조를 변경시켜야지 될 때가 있을때.. 계속해서 모자를 바꿔쓰면서 나중에는 어떤 모자를 쓰고 있는지를 헛갈릴때가 많은 것 같은데...

좀더 어떤 일을 하고 있는지에 대한 자신에게 명확히 하는 것이 좋을듯. 쉬워보이는 말이지만 매우매우 어려운 이야기.
Posted by Y2K
,

1. Facade Pattern

·         새로운 Class 인터페이스의 정립

·         복잡한 시스템을 쉽게 사용한다.

·         시스템의 부분만을 이용하거나 특별한 방법으로 시스템을 이용하도록 한다.

·         보다 단순하고, 사용하기 쉬운 시스템 또는 요구에 맞는 최적화된 시스템을 갖게 된다.

Facade 패턴 : 핵심 특징
의도 : 기존에 존재하고 있던 시스템을 이용하는 방법을 단순화 하고 싶다. -> 자신만의 인터페이스를 정의한다.

·         문제점 : 복잡한 시스템의 오직 부분만을 이용해야지 된다. 또는 특별한 방법으로 시스템과 상호작용 되어야 한다.

·         해결책 : 기존에 존재하고 있던 시스템을 이용하려고 하는 Client 위한 새로운 인터페이스를 제시한다.

o    새로운 Class에서는 기존의 Class 소유(has - a)하게 된다.

o    새롭게 외부로 노출된 방법을 통해서, 기능을 구현하게 된다.

·         참여자와 협력자 : Client 좀더 사용하기 쉽도록 특별한 인터페이스를 제시한다.

·         결과

o    필요한 Sub System 이용을 단순화 한다. : 부분만을 이용하고, 특별한 방법으로의 시스템과 상호작용되기 때문

o    모든 기능을 처리하지는 않는다.

·         구현

o    필요한 인터페이스를 갖는 새로운 Class 정의한다.

o    새롭게 만들어진 Class에서 기존에 존재하던 시스템을 이용하게 된다.

 

2. Adapter Pattern

·         새로운 인터페이스를 만들자.

o    올바른 작업을 수행하지만, 잘못된 인터페이스를 가진 객체를 위해 새로운 인터페이스를 생성하기 위한 방법이 필요하다.

·         Client 객체가 상세한 사항을 필요가 없게 하자.

 Adapter 패턴 : 핵심 특징

·         의도 : 통제 범위 이면에 있는 기존에 존재하고 있던 객체를 특별한 인터페이스와 조화시키자.

·         문제점 : 시스템은 올바른 데이터와 행위를 가지고 있지만, 잘못된 인터페이스를 가지고 있다. 일반적으로 정의하고 있거나 이미 정의된 추상 클래스의 파생 클래스를 만들기 위해 사용된다.

·         해결법 : Adapter 패턴은 원하는 인터페이스를 가진 래퍼를 제공한다.

·         여자와 협력자 : Adapter 자신의 Target 클래스(Adapter 클래스는 Target 클래스로부터 파생) 가진 인터페이스와 조화시키기 위해 Adaptee 클래스의 인터페이스를 적응시킨다. 이는 Client Adaptee 마치 Target 나잎인것 처럼 사용할 있게 된다.

·         결과 : 이미 존재하는 객체들이 그들의 인터페이스에 의해 제한받지 않고 새로운 클래스 구조에 맞출수 있게 된다.

·         구현 :  다른 클래스에 기존에 존재하고 있던 클래스를 포함시킨다. 포함하는 클래스는 요구되는 인터페이스에 조화시키고 포함되는 클래스의 메소드를 호출한다.

 

3. Bridge Pattern

·         Bridge 패턴에 통합된 Adapter 패턴을 보게 되는 것일 일반적

·         합성 디자인 패턴으로 구현되는 경우가 많다.

 

Bridge 패턴의 핵심 특징
편집 부분

·         의도 : 구현을 이용하는 객체의 집합으로부터 구현의 집합을 분리한다.

·         문제점 : 추상클래스의 파생 클래스가 폭발적으로 증가되는 것을 막는다.

·         해결법 : 이용하고자 하는 모든 구현에 대한 인터페이스를 정의하고, 구현을 이용하는 추상 클래스의 파생 클래스를 갖는다.

·         여자와 협력자 : 구현되고 있는 객체에 대해 인터페이스를 정의한다. Implementor 특정한 구현 클래스에 대해 인터페이스를 정의한다. Abstraction으로부터 파생 클래스는 어떤 특정한 ConcreteImplementor 이용되는지 알지 못하면서 Implementor로부터 파생 클래스를 이용한다.

·         결과 : 구현을 이용하는 객체로부터 구현을 분리하는 것은 확장성을 높여준다. 클라이언트 객체는 구현에 대한 사항을 알지 못한다.

·         구현

o    추상 클래스에 구현을 캡슐화 한다.

o    구현되고 있는 추상화 개념의 기본 클래스 안에 구현에 대한 처리를 포함한다.

 

 

4. Abstract Factory

·         시스템이 항상 자기의 상황을 위해 정확한 객체를 얻도록 보증한다.

Abstract Factory 패턴 : 핵심 특징
편집 부분

·         의도 : 특정 클라이언트를 위한 객체 집합이나 군을 갖기를 원한다.

·         문제점 : 관련된 객체 군은 인스턴스화 되어야 한다.

·         참여자와 협력자 : AbstractFactory 요구되는 객체 군의 맴버를 생성하는 방법을 위한 인터페이스를 정의한다. 일반적으로, 각각의 군은 자신의 유일한 ConcreteFactory 가짐으로 객체로 생성된다.

·         결과 : 패턴은 객체를 이용하는 방법에 대한 로직으로부터 어떤 객체를 이용할것인가 하는 규칙을 분리한다.

·         구현 : 어떤 객체가 만들어지는지를 명시한 추상 Class 정의하자. 그런 다음 객체들의 군을 위해 하나의 구체적인 클래스를 구현하자.

 

 

5. Stratege Pattern

·         GOF에서 제시한 원칙에 의해서 만들어진다.

·         새로운 요구사항을 처리하는 방법으로 사용

o    객체의 재사용을 위해 상속을 최대한 피해서 사용하자.

o    상속이 아닌, 소유로서 구현한다. : 변화하는 개념을 캡슐화 한다.

Strategy 패턴 : 핵심 특징

·         의도 : 서로다른 비지니스 규칙 또는 알고리즘을 만들어 이들이 발생하는 전후 관계에 맞게 이용할 있게 해준다.

·         문제점 : 알고리즘의 선택은 클라이언트의 요청에 맞게 데이터의 행동에 맞게 적용되어야지 된다. 단순히 변경되지 않는 규칙만을 가지고 있다면 Strategy 패턴은 필요가 없다.

·         해결법 : 알고리즘의 구현과 선택을 분리하자. 전후 관계를 기반으로 선택을 하도록 하자

·         참여자와 협력자

o    Strategy 다른 알고리즘이 어떻게 이용되는지 명시한다.

o    ConcreteStrategie들은 이런 다른 알고리즘을 구현한다.

o    Context Strategy 타잎의 레퍼런스로 구체적인 ConcreteStrategy 이용한다.

·         결과

o    Strategy 패턴은 알고리즘의 군을 형성한다.

o    switch if/else 조건문이 제거된다.

o    모든 알고리즘이 동일한 방법으로 호출되어야지 된다.

·         구현

o    알고리즘을 호출하는 방법을 명시하는 추상 메소드를 갖는 추상클래스를 포함하는 클래스를 갖는다. : SerialPolling

o    각각의 파생 클래스는 필요에 따라 알고리즘을 구현한다. : StrignParser

 

 

 

Posted by Y2K
,

최소 지식 원칙

Book 2009. 1. 7. 11:56

최소 지식 원칙에서의 가이드라인

1. 객체 자체만의 호출

2. 메소드에서 매개 변수로 전달된 객체

3. 그 메소드에서 생성하거나 인스턴스를 만든 객체

4. 그 객체에 속하는 구성 요소

  

public static double GetTemplrature()

{

  // NOTE : 틀린 경우

  Thermoeter thermometer = station.GetThermometer();

  return thermometer.GetTemperature();

}


public static double GetTemperature()

{

  //NOTE : 맞는 경우 

  return station.GetTemperature();

 } 

Posted by Y2K
,

구상되는 기본틀은 모두 Template를 이용하고, Template에서 이용되는 method들을 따로 객체화 시킨다.

  

namespace TemplatePattern

{

  public abstract class SYNCmailBasePannel

  {

    public abstract void LoadPannel();

    public abstract void MoveNextPannel();

    public abstract void MovePreviousPannel();


    public void ArrangeSubControls()

    {

      //NOTE : arrange sub controls

    }


    public void PaintPanel()

    {

      //NOTE : paint panel

    }

  }


  #region Using Strategy Pattern

  public interface ILoginService

  {

    bool Login(string logonName, string password);

  }


  public interface IChangeDisplayName

  {

    bool ChangeDisplayName(string displayName);

  }

   

  public class SYNCmailLoginService : ILoginService

  {


    #region ILoginService Members


    public bool Login(string logonName, string password)

    {

      throw new NotImplementedException();

    }


    #endregion

  }


  public class SYNCmailChangeDisplayNamer : IChangeDisplayName

  {

    #region IChangeDisplayName Members


    public bool ChangeDisplayName(string displayName)

    {

      throw new NotImplementedException();

    }


    #endregion

  }


  #endregion


  public class SYNCmailDisplayPannel : SYNCmailBasePannel

  {

    public IChangeDisplayName iChangeDisplayName;


    public override void LoadPannel()

    {

      throw new NotImplementedException();

    }


    public override void MoveNextPannel()

    {

      iChangeDisplayName.ChangeDisplayName("newDisplayName");

    }


    public override void MovePreviousPannel()

    {

      throw new NotImplementedException();

    }

  }


  public class SYNCmailLoginPannel : SYNCmailBasePannel

  {

    public ILoginService iLoginService;


    public override void LoadPannel()

    {

      //TODO : GET Login information

    }


    public override void MoveNextPannel()

    {

      //TODO :

      // 1. Connection SYNCmail API

      // 2. Check Login Informations

      if ( iLoginService.Login("logonName", "password") )

      {

        //NOTE : Move Next Pannel

      }

    }


    public override void MovePreviousPannel()

    {

      //TODO :

      // Move previous Pannel

    }

  }

}

Posted by Y2K
,

Collection 구현 방법을 노출하지 않으면서 Collection 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법의 제공

  

항목들에 대한 반복 작업을 일괄적으로 할 수 있도록 해준다.

  

.NET Framework :

System.Collections.IEnumerable  : GetEnumerator()를 통해서 IEnumerator를 얻어낸다.

System.Collections.IEnumerator : 객체에 대한 접근 항목을 보여주는 interface. Current, MoveNext, Reset을 통해서 모든 객체에 대한 일괄적인 접근제어를 한다. 

  

  public class DinnerMenus2 : IEnumerable

  {

    #region IEnumerable Members


    public IEnumerator GetEnumerator()

    {

      throw new NotImplementedException();

    }


    #endregion

  }



  public class DinnerMenus : IEnumerator

  {

    private int position;


    public DinnerMenus()

    {

      position = 0;

    }


    #region IEnumerator Members


    public object Current

    {

      get { throw new NotImplementedException(); }

    }


    public bool MoveNext()

    {

      throw new NotImplementedException();

    }


    public void Reset()

    {

      throw new NotImplementedException();

    }


    #endregion

  }

Posted by Y2K
,

공통적으로 일하는 것과 각기 다른 객체들간의 차이점을 나누는 것부터 시작한다. 

Class의 Method에서 알고리즘의 골격을 만들어주고, 몇몇 단계를 sub-class에서 이용하는 방식.

옛날부터 내가 자주 사용하던 pattern.  (잘 보면 그전까지 하던 일들에 대해서 용어를 붙이는 느낌이 든다.;;)

  

namespace TemplatePattern

{

  public abstract class CaffeinBeverage

  {

    public void PrefareRecipe()

    {

      BoilWater();

      Brew();

      PourInCup();

      AddCodiments();

    }


    protected abstract void Brew();

    protected abstract void AddCodiments();


    protected void BoilWater()

    {

      Console.WriteLine("Boil Water");

    }


    protected void PourInCup()

    {

      Console.WriteLine("Pour in Cup");

    }

  }


  public class CoffeeBeverage : CaffeinBeverage

  {

    protected override void Brew()

    {

      Console.WriteLine("Brew Coffee Grinds");

    }


    protected override void AddCodiments()

    {

      Console.WriteLine("Add Sugar");

    }

  }


  public class TeaBeverage : CaffeinBeverage

  {

    protected override void Brew()

    {

      Console.WriteLine("Brew Tea Grinds");

    }


    protected override void AddCodiments()

    {

      Console.WriteLine("Add Lemon");

    }

  }

Posted by Y2K
,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StrategyPattern
{
  /// <summary>
  /// Strategy pattern을 이용. : SYNCmail Mobile도 같은 방법으로 구현해보는 것이 어떨까 고민되고 있음.
  /// </summary>
  public abstract class OperationPanel
  {
    public IOperation Operation { get; set; }
    public void DoOperation()
    {
      Operation.DoMainOperation();
    }
  }

  public interface IOperation
  {
    void DoMainOperation();
    void DoSubOperation();
    void LoadOperation();
  }

  public class CheckPasswordOperation : IOperation
  {
    public string LogonName { get; set; }
    public string Password { get; set; }

    public void DoMainOperation()
    {
      Console.WriteLine("Check Password");
    }

    public void DoSubOperation()
    {
      Console.WriteLine("Cancel operation");
    }

    public void LoadOperation()
    {
      Console.WriteLine("Display");
    }
  }
  //abstract class의 이용을 하지 않고, Interface의 구현 및 조합을 통해서, 각각의 class의 구현을 행한다.
  //abstract method를 사용하지 않고, abstract method에서 사용되는 동작의 정의를 Interface를 통한다. 그 interface의 구현을 통해서 만들어주는 패턴

}
Posted by Y2K
,

개인적으로 가장 많이 사용한 패턴.

DB에 대한 connection 문제 해결이라던지, 하나의 리소스를 사용하는 방법으로 주로 사용한다.

특히 윈도우에서는 Registery의 참조부분을 행할때 주로 사용하게 된다.

  

.NET에서 keyword인 volatile를 사용한 것을 주목할것.

  

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;


namespace SingletonPattern

{

  public class SingletonData

  {

    private static volatile SingletonData singletonInstance;


    public static SingletonData GetInstance()

    {

      if ( singletonInstance == null )

      {

        singletonInstance = new SingletonData();

      }

      return singletonInstance;

    }


    private SingletonData()

    {

    }

  }

}

Posted by Y2K
,