잊지 않겠습니다.

IOldBookStoreSystem을 구현한 OldBookStore을 아래와 같이 구현되어 있는 NewBookStore에서 사용하고자 할때. 

같은 또는 비슷한 기능들이 각기 다른 interface로 구현되어 있을때 사용하는 pattern이 된다.

  

 

 public class NewBookStore

 {

   public INewBookStoreSystem bookStoreSystem;

 } 

  

  public interface INewBookStoreSystem

 {

   void BuyBook(string isbnNumber, string cardNumber);

   void ChangeBook(string cardNumber);

 } 

  

//OldBookStore

  public interface IOldBookStoreSystem

  {

    void BuyBook(string bookTitle, int costMoney);

    void ChangeBook(string bookTitle, DateTime buyDate);

    void CloseStore();

  }

  

  public class OldBookStore : IOldBookStoreSystem

  {

    public void BuyBook(string bookTitle, int costMoney)

    {

      Console.WriteLine("Buy book Title");

    }


    public void ChangeBook(string bookTitle, DateTime buyDate)

    {

      Console.WriteLine(string.Format("Change Book : {0}", buyDate));

    }


    public void CloseStore()

    {

      Console.WriteLine("Close the store.");

    }

  } 

  

이때에 새로운 INewBookStoreSystem을 이용. (INewBookStoreSystem이 adapter interface가 된다.)

  public class OldBookStoreAdapter : INewBookStoreSystem

  {

    OldBookStore oldBookStore = new OldBookStore();

    #region INewBookStoreSystem Members


    public void BuyBook(string isbnNumber, string cardNumber)

    {

      //NOTE : Find book title using isbn number

      string bookTitle = string.Empty;

      //NOTE : send bill

      int price = 1000;

      oldBookStore.BuyBook(bookTitle, price);

    }


    public void ChangeBook(string cardNumber)

    {

      //NOTE : Find card list using cardNumber

      //NOTE : Find bookTItle and buyDate


      DateTime buyDate = DateTime.Now;

      string bookTitle = string.Empty;


      oldBookStore.ChangeBook(bookTitle, buyDate);

    }


    #endregion

  } 

Posted by Y2K
,

Head First Design Patterns

Book 2009. 1. 7. 11:39

스토리가 있는 패턴 학습법

 

    

Design pattern에 대한 책은 찾아보면서 여러가지로 생각도 많이 들고, 보고 나서도 어떻게 사용해야지 될지 모르겠다는 생각이 무척 많이 든다. C# 3.0 Design pattern은 다 보고 나서, 과연 어디에 사용할 수 있을지 모르겠다는 생각만 무척 많이 들고, 상속을 이용한 구성만이 계속 머리에 남고 있을때 보기 시작한 책.

    

이번에는 책 한권 제대로 정리좀 해봐야지 될텐데. --; 

   

   

1. 바뀌는 부분은 캡슐화 된다.

2. 상속보다는 구성을 이용한다. (!! 가장 중요한 일이며, 가장 안지켜지는 일. -_-)

3. 구현이 아닌 interface에 맞춰서 프로그래밍한다.

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

5. Class는 확장에 대해서는 열려있어야지만 변경에 대해서는 닫혀있어야지 된다.

6. 추상화된 것에 의존하라. 구상 class에 의존하지 않아야지 된다. (2번 항목과 같이 연결되는 부분. 이 부분에 대한 내용을 좀더 고민할 필요가 있음.) 

  

Design Pattern에 대해서 공부를 할때마다 느끼는 가장 큰 하나.

: 공부할때, 내가 이런 방법이 코딩에 좋겠다.. 싶어서 예전에 썼던 것들이 다 패턴이였구나.. 하는 생각이 들때가 많다. --;

SYNCmailMobile이 CommandPattern을 만들어서 구성된 것을 이제야 깨닫게 되었다니.. -_-;; 

Posted by Y2K
,

Structural Pattern

Book 2009. 1. 7. 11:37

Structual Pattern은 system이 design이 될때, 또는 유지보수 또는 확장시에 고려되게 된다. 

특히 구버젼 시스템에 대한 확장시에 특히 유용하게 된다.  새로운 요구사항이 들어왔을 때에, 기존의 Class를 변경하는 것이 아닌, Decorator pattern을 이용해서 기존의 class를 유지하는 형태로 구현되게 된다. 

 

  • 존재하는 객체에 대해서 빈번하게 새로운 작업이 행해지게 될때,(decorator)
  • 객체에 대한 동작을 행할때, (proxy)
  • 요구시에 비용이 많이 드는 객체를 생성할때, (proxy)
  • interface의 개발과 process에 의존적인 component를 구현할때, (bridge)
  • 서로 양립되지 않는 interface를 서로간에 일치시킬 때 (adapter)
  • 작고 많은 객체들의 비용을 줄일때 (flyweight)
  • 하나의 entry point를 갖는 여러개의 subsystem을 새롭게 개편시킬 때 (Facade)
  • 실행시에 객체를 선택 또는 변경시킬때 (Bridge)
  • 복잡한 subsystem을 간략화 시킬때 (Facade)
  • single objects와 composite object를 하나의 방법으로 다룰때(Composite) 

 

Decorator Pattern

Role

새로운 상태나 동작을 객체에 주기 위한 Pattern.  구현에서 가장 생각해야지 될 내용은 원 class의 상속과 class의 instance 생성을 포함한다는 것이다.

 

Illustration

Decorator pattern은 존재하는 object에 새롭게 더하는 것이 된다.

원 객체는 decorations에 대해서 알지 못하고, 그것에 대하여 관여하지 않는다.

모든 객체는 모든 option을 가지지 않고, 큰 일을 하나가 도맡아서 하지 않는다.

모든 decorations는 서로간에 연관되게 된다.

docorations는 서로간에 결합될 수 있고, mix-and-match가 되게 된다.

 

example

1. 하나의 그림이 있고, 그 그림에 tag를 달 수가 있다. tag를 하나 이상을 달 수 있으며, 그 tag는 각기 다른 tag를 다는 것이 가능하다.

 using System;

namespace PhotoDecorator
{
  public interface IPhoto
  {
    void Draw();
  }

  public class Photo : IPhoto
  {
    public void Draw()
    {
      Console.WriteLine("Draw Original Photo!!");
    }
  }

  public class Tag1 : IPhoto
  {
    IPhoto component;

    public Tag1(IPhoto component)
    {
      this.component = component;
    }
    public void Draw()
    {
      component.Draw();
      Console.WriteLine("Draw tag1 into photo");
    }
  }

  public class Tag2 : IPhoto
  {
    IPhoto component;

    public Tag2(IPhoto component)
    {
      this.component = component;
    }

    public void Draw()
    {
      component.Draw();
      Console.WriteLine("Draw tag2 into photo");
    }
  }

  public class Client
  {
    public static void DrawPicture(string pictureName, IPhoto photo)
    {
      Console.WriteLine( string.Format("Picture : {0}", pictureName));
      photo.Draw();
    }
  }

  public class Program
  {
    public static void Main(string[] args)
    {
      Photo originalPhoto1 = new Photo();
      Photo originalPhoto2 = new Photo();

      Tag1 tag1WithPhoto = new Tag1(originalPhoto1);
      Tag2 tag2WithPhoto = new Tag2(originalPhoto2);

      Client.DrawPicture("Original Photo with Tag1", tag1WithPhoto);
      Client.DrawPicture("Original Photo with Tag2", tag2WithPhoto);

      Console.ReadLine();

    }
  }
}

2. windows를 하나 설치 한 후에, MS SQL과 Visual Studio를 설치시켜줘야지되는 그러한 환경에 대한 구현을 생각해보자.

 using System;


namespace InstallWindows
{
  public interface IInstall
  {
    void Install();
  }

  public class WindowsInstaller : IInstall
  {
    #region IInstall Members

    public void Install()
    {
      Console.WriteLine("Windows Install complete");
    }

    #endregion
  }

  public class MsSqlInstaller : IInstall
  {
    IInstall windowsComponent;

    public MsSqlInstaller(IInstall component)
    {
      windowsComponent = component;
    }

    #region IInstall Members

    public void Install()
    {
      windowsComponent.Install();
      Console.WriteLine("MSSQL install complete!!");
    }

    #endregion
  }

  public class MsVisualStudioInstaller : IInstall
  {
    IInstall windowsComponent;
    public MsVisualStudioInstaller(IInstall component)
    {
      windowsComponent = component;
    }

    #region IInstall Members
    public void Install()
    {
      windowsComponent.Install();
      Console.WriteLine("MS Visual Studio Install complete!!");
    }
    #endregion
  }


  public class Client
  {
    public static void InstallWIndows(string target, IInstall component)
    {
      Console.WriteLine("====={0}=====", target);
      component.Install();
    }
  }

  public class Program
  {
    public static void Main()
    {
      WindowsInstaller windows1 = new WindowsInstaller();  //windows와 ms sql을 설치
      WindowsInstaller windows2 = new WindowsInstaller();  //windows와 ms visual studio를 설치
      WindowsInstaller windows3 = new WindowsInstaller();  //windows와 ms sql, ms visual studio를 설치

      MsSqlInstaller sqlInstaller = new MsSqlInstaller(windows1);
      Client.InstallWIndows("Windows with ms sql", sqlInstaller);

      MsVisualStudioInstaller vsInstaller = new MsVisualStudioInstaller(windows2);
      Client.InstallWIndows("Windows with visual studio", vsInstaller);

      MsVisualStudioInstaller vsAndSqlInstaller = new MsVisualStudioInstaller(new MsSqlInstaller(windows3));
      Client.InstallWIndows("Windows with ms sql and visual studio", vsAndSqlInstaller);

      Console.ReadLine();
    }
  }
}

 

Use

  • 하나의 기본적 동작이 있고, 그 동작에 대한 추가 동작에 대하여 만드는 것이 가능하다. (위의 예제와 같이, 윈도우를 하나 설치하고, 그 다음에 다른 동작을 행할 때, 그 윈도우에 대하여 동작을 넣는 것이 가능하다.)
  • C#의 I/O API가 Decorator pattern으로 되어 있다. System.IO.Stream에서 다른 Stream들은 IStream을 생성자로 받아서 동작을 행하도록 되어있다.
  • Windows control의 경우에, System.Windows.Control을 상속받는 class들은 모두 Decorator pattern으로 만들어져 있다. ListBox를 그린 후에 Border를 진하게 만들어 준다던가 하는 식의 동작들은 모두 Decorator pattern으로 구성되게 된다.

 

Summary

You have :
  • 기존에 존재하는 component가 subclassing이 불가능 할때.
You want to:
  • 추가적 상태나 동작을 추가하고 싶을때
  • 다른 class에 영향을 주지 않고, class에 변경을 가하고 싶을때,
  • 너무 많은 class가 영향을 받을 수 있기 때문에 subclassing을 피하고 싶을때
But consider using instead
  • 다른 class 사이에 interface를 넣는 adapter pattern이 될때
  • Composite pattern : interface의 상속없이 다른 class를 구현해서 동작의 결합이 가능할 때.
  • Proxy pattern : object에 대한 동작을 특별하게 만들어줘야지 될때.
  • Strategy pattern : wrapping하기 보다는 original object를 변경시키는 것이 더 나을때. 

 

 

Proxy Pattern

Role

객체에 대한 생성 및 접근을 제한을 줄 필요가 있을때 주로 사용된다. 이때 Proxy의 경우에는 작은 public method로 열린 객체로서 만들어지고, proxy로 보호되는 객체는 private로 닫힌 객체로서 생성 및 접근에 제한을 주게 된다.

 

Illustration

  Logon 되지 않은 상태에서는 View 상태만을 제공하고, 객체에 대한 생성 및 접근은 Logon이 된 상태에서 이루어지는 것과 비슷하게 구현되면 된다.

 

Example

1. Logon이 되지 않은 상태와 Logon이 된 상태에서의 View와 객체의 Request 의 제한을 주는 경우

using System;

namespace ProxyPattern
{
  public interface ISubject
  {
    void DoSomeThing();
  }

  internal class Subject
  {
    public void DoSomeThing()
    {
      Console.WriteLine("Do SomeThing in wanted object");
    }
  }

  public class ProtectionSubject : ISubject
  {
    private Subject subject; //Proxy에 의해서 접근 및 생성이 제한 받는 Class

    public bool Authenticate(string password)
    {
      if ( password.ToLower() == "password" )
      {
        subject = new Subject();
        return true;
      }
      else
      {
        subject = null;
        return false;
      }
    }
    #region ISubject Members

    public void DoSomeThing()
    {
      if ( subject != null )
      {
        subject.DoSomeThing();
      }
      else
      {
        Console.WriteLine("Authenticate First!!");
      }
    }

    #endregion
  }

  public class Program
  {
    public static void Main(string[] args)
    {
      ProtectionSubject protectionSubject = new ProtectionSubject();
      protectionSubject.DoSomeThing();
      protectionSubject.Authenticate("윤영권");
      protectionSubject.DoSomeThing();
      protectionSubject.Authenticate("password");
      protectionSubject.DoSomeThing();

      Console.ReadLine();
    }
  }
} 


2. Install이 되지 않은 상태에서 Computer를 접근하는 경우 (proxy와 decorator를 둘다 적용한 경우)

using System;

namespace ProxyAndDecorator
{
  public interface IWindowProxy
  {
    void DoSomeThing();
  }

  public interface IInstall
  {
    void Install();
  }

  public class WindowsProxy : IWindowProxy
  {
    IInstall installObject;

    public void CreateObject(string type)
    {
      if ( type == "none" )
      {
        installObject = new WindowsInstaller();
      }
      else if ( type == "mssql" )
      {
        WindowsInstaller windowsInstaller = new WindowsInstaller();
        installObject = new MsSqlInstaller(windowsInstaller);
      }
      else if ( type == "vs" )
      {
        WindowsInstaller windowsInstaller = new WindowsInstaller();
        installObject = new MsVisualStudioInstaller(windowsInstaller);
      }
    }

    #region IWindowProxy Members
    public void DoSomeThing()
    {
      if ( installObject != null )
      {
        IWindowProxy w = ( IWindowProxy )installObject;
        w.DoSomeThing();
      }
      else
      {
        Console.WriteLine("First CreateObject");
      }
    }
    #endregion
  }

  public class WindowsInstaller : IInstall, IWindowProxy
  {
    internal WindowsInstaller()
    {

    }
    #region IInstall Members
    public void DoSomeThing()
    {
      Console.WriteLine("Do Something in original windows");
    }

    public void Install()
    {
      Console.WriteLine("Windows Install complete");
    }

    #endregion
  }

  public class MsSqlInstaller : IInstall, IWindowProxy
  {
    IInstall windowsComponent;

    internal MsSqlInstaller(IInstall component)
    {
      windowsComponent = component;
    }

    #region IInstall Members

    public void Install()
    {
      windowsComponent.Install();
      Console.WriteLine("MSSQL install complete!!");
    }

    #endregion

    #region IWindowProxy Members

    public void DoSomeThing()
    {
      Console.WriteLine("Do Something in ms sql");
    }

    #endregion
  }

  public class MsVisualStudioInstaller : IInstall, IWindowProxy
  {
    IInstall windowsComponent;
    internal MsVisualStudioInstaller(IInstall component)
    {
      windowsComponent = component;
    }

    #region IInstall Members
    public void Install()
    {
      windowsComponent.Install();
      Console.WriteLine("MS Visual Studio Install complete!!");
    }
    #endregion

    #region IWindowProxy Members

    public void DoSomeThing()
    {
      Console.WriteLine("Do SomeThing in Visual Studio");
    }
    #endregion
  }


  public class Program
  {
    public static void Main()
    {
      WindowsProxy proxy = new WindowsProxy();

      proxy.DoSomeThing();

      proxy.CreateObject("none");
      proxy.DoSomeThing();

      proxy.CreateObject("mssql");
      proxy.DoSomeThing();

      proxy.CreateObject("vs");
      proxy.DoSomeThing();

      Console.ReadLine();
    }
  }
} 

 

Use

Proxy는 class에 접근할 수 있는 곳에 위치하게 된다. 이때 class는 민감한 데이터 영역이나, 느린 동작을 가진 것들을 주로 사용하게 된다. 대체적으로 Proxy로 사용되는 사항에서는 생성에서 무언가 제한을 준다던가, 데이터의 접근시에 무언가 제한을 줘야지 될때 주로 사용되어야지 된다.

 

Summary

You have objects that:
  • 생성에 비용이 많이 든다.
  • 접근제한이 필요하다.
  • Remote Site에서의 접근이 필요하다.

 

You want to:
  • 객체의 생성은 반드시 하나 이상의 동작을 통해서만 이루어지기를 원할때,
  • 객체의 접근에 대한 확인을 요구할때,
  • Local 객체가 remote 객체에 의한 접근을 행할때
  • 객체에 대한 접근 권한 설정을 줄때

 

 

Bridge Pattern

Role

Bridge Pattern은 구현과 추상화의 결합도를 낮추는데 목표가 있다. Bridge pattern은 기존에 존재하고 계속 남아있어야지 되는 구버젼에서 새로운 신버젼이 있을때에 유용한 패턴이다 .이미 구현된 추상화에 맞추어진 client code는 변경되지 않지만 client code는 자신이 원하는 어떤 버젼에서 동작될지를 결정해줘야지 된다.

Illustration

여러개의 .NET Framework가 있는 상황에서, S/W가 어떠한 .NET Framework를 골라서 사용해야지 될지를 고민하는 상황과 비슷하게 만들어진다.

Design

상속은 각기 다른 추상화의 구현에 대하여 일반적인 방법이다. 그렇지만, 구현은 추상화의 영역에 제한이 되게 되며, 추상화의 의존적인 상태에서의 변경은 제한적이 된다. Bridge pattern은  하나 이상의 추상화에 대한 상속을 통해 이미 구버젼의 추상화가 되어 있는 상황에서 추상화에 상속을 하는 것이다.  같은 추상화에서의 각각의 상속을 통해 추상화의 범위를 넓히는 것이 가능하다.

구현에 대한 동작을 Bridge에 위임하고, 동작에 대한 것을 따로 나누어주는 것으로서 구현하게 되는데, Bridge를 개선하는 것으로 전체 Class의 변경 없이 구현이 가능해진다.  

 

using System;

namespace BridgePattern
{
  public interface IBridge
  {
    void Add(string message);
    void Add(string friend, string message);
    void Poke(string who);
  }

  /// <summary>
  /// Bridge에 의하여 구현되어진 Class
  /// 구현에 의한 추상화만을 가지고 있고, 실질적인 동작은 Bridge에서 이루어지게 된다. 
  /// 같은 추상화에서 다른 동작을 행할때, Bridge를 변경시켜서 가능해진다.
  /// </summary>
  class Portal
  {
    readonly IBridge bridge;
    public Portal(IBridge bridge)
    {
      this.bridge = bridge;
    }

    public void Add(string message)
    {
      bridge.Add(message);
    }

    public void Add(string friend, string message)
    {
      bridge.Add(friend, message);
    }

    public void Poke(string who)
    {
      bridge.Poke(who);
    }
  }

  /// <summary>
  /// Proxy에 의해서 보호받는 Class
  /// </summary>
  class Subject
  {
    public string SubjectName { get; set; }
    public void DisplayName()
    {
      Console.WriteLine(SubjectName);
    }
  }

  /// <summary>
  /// Proxy
  /// </summary>
  class MySubject
  {
    private Subject subject;
    public void Authritize(string password)
    {
      subject = new Subject();
    }
    public void DisplayName()
    {
      if ( subject == null )
      {
        Console.WriteLine("Subject is null");
      }
      else
      {
        subject.DisplayName();
      }
    }
  }

  /// <summary>
  /// Bridge Pattern으로 만들어진 Class
  /// 실질적인 동작이 이루어지는 Class
  /// </summary>
  public class MyBook : IBridge
  {
    public string Name { get; set; }
    private MySubject mySubject;
    private static int users;

    public MyBook(string name, string password)
    {
      mySubject = new MySubject();
      Name = name;
      users++;

      mySubject.Authritize(password);
    }

    #region IBridge Members

    public void Add(string message)
    {
      mySubject.DisplayName();
      Console.WriteLine(message);
    }

    public void Add(string friend, string message)
    {
      mySubject.DisplayName();
      Console.WriteLine(string.Format("{0} : {1}", friend, message));
    }

    public void Poke(string who)
    {
      mySubject.DisplayName();
      Console.WriteLine(string.Format("{0}", who));
    }

    #endregion
  }

  /// <summary>
  /// C# 3.0 Extension : Class Extension, Bridge pattern에서 Bridge의 확장에 유용하게 사용할 수 있다.
  /// </summary>
  static class OpenBookExtensions
  {
    public static void SuperPoke(this Portal me, string who, string message)
    {
      me.Add(who, message);
    }
  }

  class BridgePattern 
  {
    public static void Main(string[] args)
    {
      Portal me = new Portal(new MyBook("Judith", "password"));
      me.Add("Hello world");
      me.Add("Today I worked 18 hours");
      Portal tom = new Portal(new MyBook("Tom", "password2"));
      tom.Poke("Judith-1");
      tom.SuperPoke("Judith-1", "hugged");
      tom.Add("Judith-1", "Poor you");
      tom.Add("Hey, I'm on OpenBook - it's cool!");
    }
  }
} 

Use

Bridge Pattern은 매우 간단하지만, 매우 강력한 Pattern이 된다. 하나의 구현에서 다른 구현을 이루고, 같은 추상화를 통해서 초기 디자인을 고려할 수 있기 때문이다. 잘 알려진 Bridge pattern은 Graphics에서 주로 사용된다. 각기 다른 Graphic 환경에서 다른 방법으로의 Display는 하나의 Display에 대한 추상화에서 각기 다른 Bridge를 통해서 Graphics를 구현하는 것이 가능하다.

You can:

Operation에 대한 구현을 언제나 알 필요가 없는 상태에서 Operation을 정의할 수 있다. 

You want to:
  • Client로부터 구현을 완벽하게 감출수 있다. (하나의 추상화에서 각기 다른 구현이 가능)
  • 추상화와 구현의 binding을 피할수 있다.
  • 추상화의 변경 없이, 구현을 변경하는 것이 가능하다.
  • 실행시에 각기 다른 Part와 연결이 가능하다. 
Posted by Y2K
,

Pattern Comparison

Book 2009. 1. 7. 11:37


Summary :

Decorator pattern

하나의 동작(interface)에서 각기 다른 동작이지만, 서로간에 영향을 줄 수 있는 상황에서 사용한다. 객체의 생성자에 다른 같은 interface를 갖는 객체를 받아서, 하나의 interface에서 같은 동작을 행하도록 만들어준다. 이러한 방법은 각각의 객체에 자신이 하는 일을 구현하게 되며, 마지막으로 decorate 된 객체에서 모든 객체에 대한 동작을 일괄적으로 처리할 수 있도록 해준다. 또 다른 추가적인 동작이 필요할 때, 다른 decorator를 만들어서, 그 decorator를 사용하면 모든 객체에 대한 동작을 일괄적으로 만들어 줄 수 있다.

 

Proxy pattern

하나의 객체에 대한 접근과 생성을 제한할 필요가 있을때 사용한다. Proxy 객체의 경우에는 작은 public method를 가지게 되며, Proxy 객체에서 보호되고 있는 객체에 대한 접근 권한과 생성을 도맡아서 한다. 주로 Proxy 객체에서 private로 가진 객체의 보호를 하게 된다.

 

Bridge pattern

추상화와 구현을 분리하는 방법으로, 추상화를 통해서 미리 interface가 구현되어 있는 상황에서 각각의 Bridge를 새로 만들어서 구현을 해주는 방법이다. 주로 추상화와 구현을 완전히 분리하고, Runtime시에 다른 구현을 사용할 수 도 있는 방법으로 사용된다.  

Posted by Y2K
,

In C++ or No GC enviroment, we have two serious problems in windows programming.

  • deletes other objects to which the object holds pointers
  • closes handles to resource

And .NET cannot release unmanaged resource automatically. so .NET prepared Finalize() method to dipose unmanaged resource.

In VB.NET : actually write the Finalize() method

In C# : pseudo-destructor with the ~NameOfYourClass() -> C# Compiler creates the Finalized() method using the code when we write in the specialized pesudo-destructor.

 

Writing Finalize() is rarely a good idea

if you don't use unmanaged resource, Don't write the Finalize() method. It make inaccessible instances and delay the cleanup.

 

Releasing managed resources in Finalize() can wreak havoc

Don't access managed resources with in the Finalize() method. If you access managed resource, it cannot be predicted.

Also, don't call any methods on other objects, or even set their reference to null/Nothing.

 

Rules to invoke base.Finalize() aren't consistent

In VB.NET, you write Finalize() method as a protect method but you only write pseudo-destructor in C#.

so you don't invoke the base class's Finalize() method in C# but you must invoke the base class's Finalize() method in VB.NET.

 

Depending on the Finalize() can tie up critical resources

.NET provides a mechanism to support synchronous cleanup fo resource used by object. : IDisposable interface

If you want to clean up your unmanaged resource synchronously, you can write Dispose() method used IDisposable interface.

 

Using Finalize() on disposed objects is costly

It's better to Dispose() an object than to depend on Finalize() method because Dispose() let you control the clean up of an object.

and Dispose() should have already taken care of its resources, both managed and unmanaged resources.

Calling Finalize() on an object involves overhead. This should be avoided as mush as possible. From within your Dispose() method, call GC.SuppressFinalize().

Posted by Y2K
,

What are the things you need to remember to make your code interoperable between language?

Common language specification complicance isn't the default

Operation overload : not Common Language Specification(CLS)

CLS-Compliant : add [Assembly:system.CLSCompliant(true)] 를 넣어줘야지 된다.

이는 Language가 다를때, 서로간에 CLS에 대해서 명확한 정의를 따르는지에 대한 Compiler level에서의 정의가 될 수 있다.

 

Optional parameters break interoperability

VB.NET에서 허용되는 option parameter의 경우에는 C#에서는 허용되지 않기 때문에, 반드시 그 값을 적어줘야지 된다.

 

Mixing case between class members breaks interoperability

VB.NET에서는 각 값의 case에 따라서 다른 Property, member, method를 가질 수 있다. 이는 VB.NET과 C#을 섞어서 사용하는 경우에 CLS를 위반하게 된다.

 

Name collision with keywords breaks interoperability

VB.NET에서 사용되는 Stop()의 경우에는 code break로 사용되기 때문에, 이름에 의한 충돌이 일어날 수 있다. 언어의 종류에 따라 method의 이름을 명명할 때 주의해야지 된다. 

 

Defining an array isn't interoperability

VB에서 자주 일어나던 문제. VB의 경우에는 Array의 크기의 선언이 아닌 Array의 max index의 선언이기 때문에 발생

int[] intDatas = new int[3]; <- size : 3
Dim intDatas() As Integer = new Integer(3) {} <- size : 4


Posted by Y2K
,

Singleton is't guaranteed process-wide

Singletone으로 만들어주는 static method의 경우에는 Process에 귀속되지 않는다.

.NET에서는 Singleton은 AppDomain에 유일한 존재로 staic 을 생성하게 된다.

   

Default performance of Data.ReadXML is poor

ReadXML의 경우에는 xsd 파일이 지정되어있지 않는 경우에는 매우 속도가 느리게 된다.

Data.ReadXML을 사용하는 경우에는 반드시 xsd 파일을 지정해줘야지 된다.

   

enum lacks type-safety

enum 형태는 int 형태에 의미를 부여할 수 있는 형태로, 사용이 매우 편한 형태이지만, int의 casting을 시켜줄때는 type-safety가 이루어지지 않기 때문에 사용에 주의를 해줘야지 된다.

   

Copy constructor hampers exensibility

Constructor에서 다른 객체의 copy를 만드는 형태의 경우에는 모든 객체의 값들을 같이 참조하는 형태로 만들어지게 된다. 따라서 하나의 객체의 값이 바뀌게 되면, 다른 객체의 값까지 바뀌게 되는 형태로 만들어지기 때문에 피하는 것이 좋다.

이러한 copy constructor의 문제는 C++, Java, .NET에서 모두 같이 발생하는 문제가 된다.

   

Clon() has limitations

"copy constructor hampers exensibility"와 동일한 문제가 발생한다.

MemberwiseClone()의 경우에도 역시 마찬가지로, 같은 문제가 발생하기 때문에, 두개의 각각 다른 id를 갖지만 같은 객체의 값을 갖는 copy본을 만들어주고 싶을 때는 주의가 필요하다.

   

Object initialization sequence isn't consistent

C#과 VB.NET에서의 Initialization sequence가 각각 다르다.

Base Class에서 someClass1을 선언과 동시에 초기화 시키고, Base Class를 상속받는 Derived Class가 someClass2를 선언과 동시에 초기화 시킬때,

C#
  1. someClass2 초기화
  2. someClass1 초기화
  3. BaseClass 초기화
  4. DerivedClass 초기화
VB.NET
  1. someClass1 초기화
  2. BaseClass 초기화
  3. someClass2 초기화
  4. DerivedClass 초기화

  

Polymorphism kicks in prematurely

Constructor에서 override가 될 수 있는 virtual 함수의 호출은 하지 않는다.

virtual 함수의 호출을 Constructor에서 시켜줄 경우에는 Derived Class에서의 초기화는 항시 Base Class의 virtual 함수를 호출하게 된다. override 된 자신의 함수를 호출하는 형태가 될 수 없다.

Posted by Y2K
,

Complier warning may not be benign

Compiler에서의 경고를 무시하지 말아라. Compiler에서의 경고는 모두다 없애는 것이 가장 우선되는 일이 된다.

Treat warnings as errors option을 활성화시키고 나서 compile하는 것이 좋다.

모든 project의 수행시에, 이 옵션을 활성화 시키고 나서 시작하는 것이 유용하다.

   

Ordering of catch processin isn't consist across languages

try~catch 구문에서 C#과 VB.NET의 동작 방법은 다르다.

C#에서는 위에서부터 아래로 넘어갈 수록, 범위가 낮은 exception class로 넘어가게 되지만, VB.NET에서는 exception class로 바로 jump 하게 된다.

static void Main(string[] args)
{
 try
 {
  AMethod();
 }
 catch(Exception ex)
 {
  Console.WriteLine(ex.Message);
 }
 catch(ApplicationException ex)
 {
  Console.WriteLine(ex.Message); <- 절대 수행될 수 없다.
 }
}

   

Type.GetType() might fail at run-time

Type.GetType() 보다는 typeof/GetType를 주로 사용하는 것이 좋다.

   

rethrow isn't consistent

catch(Exception ex)으로 잡힌 error를 throw ex로 해서 다시 던지게 될 경우에는 exception안의 정보를 잃어버릴 수 있다. 이럴때는 catch(Exception)에서 throw로 다시 던져주거나, throw ex.InnerException을 이용해서 Exception을 보내주는 것이 좋다.

   

Versioning may lead to Serialization headaches

: 이미 Serialization된 객체에 대해서 객체의 또다른 member 변수등의 변경이 생겼을 때에, 기존의 serialization의 데이터를 불러오는 데에 문제가 생기게 된다. 이때에, exception을 사용해서 하는 방법도 있으나, 가장 좋은 것은 아래 코드와 같이 Helper를 이용해서 객체의 데이터를 Type의 이름에 따라 얻어오는 방법이다.

  public class SerializationHelper
  {
    /// <summary>
    /// Desirialization이 될때에, 호출을 시켜서 새로 생성되는 객체에 값을 채워주는 함수.
    /// 각각을 enumerator에서 MoveNext로 움직이는 것으로 모든 객체의 값을 얻어온다.
    /// 만약에 구버젼의 Serialization 객체인 경우에는 그 값이 없기 때문에 채워지지 않고,
    /// 기본 값을 얻어오게 된다.
    /// </summary>
    /// <param name="theType"></param>
    /// <param name="instance"></param>
    /// <param name="info"></param>
    public static void SetData(Type theType, Object instance, SerializationInfo info)
    {
      SerializationInfoEnumerator enumerator = info.GetEnumerator();

      while(enumerator.MoveNext())
      {
        string fieldName = enumerator.Current.Name;
        FieldInfo theField =
          theType.GetField(fieldName,
                           BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public |
                           BindingFlags.NonPublic);

        if ( theField != null )
        {
          theField.SetValue(instance, enumerator.Value);
        }
      }
    }

    /// <summary>
    /// Serialization이 될 때에 불러지는 함수
    /// Instance의 모든 값들을 얻어와서 Serialization을 행한다.
    /// </summary>
    /// <param name="theType"></param>
    /// <param name="instance"></param>
    /// <param name="info"></param>
    public static void GetData(Type theType, Object instance, SerializationInfo info)
    {
      FieldInfo[] fields =
        theType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public |
                          BindingFlags.NonPublic);

      foreach(FieldInfo fieldInfo in fields)
      {
        if(!fieldInfo.IsNotSerialized)
        {
          info.AddValue(fieldInfo.Name, fieldInfo.GetValue(instance));
        }
      }
    }
  }

  

Create Web apps can be painful

blank solution을 만들어 준 후에, 그 안에 project를 만들어주는 것이 project를 만들어주는 것으로 시작하는 것보다 더 낫다.

  • 디자인 된 solution에 원하는 project를 넣는 것이 가능하다. (source의 src, test의 구분이 더 쉬워진다.)
  • 여러 solution에 결합되어 있는 project들의 관리가 보다 용의하다.

IIS에서 virtual directory를 미리 만들어두고, 웹사이트를 만들어주고, 개발을 행하는 것이 훨신더 편한 방법으로 web site의 개발이 가능해진다.

  

Naming XML documentation for IntelliSense support isn't intuitive

XML-style documentation으로 만들어진 code에 대한 xml format은 자동적으로 Visual studio의 intellisense에서 사용되어질 수 잇다. 그렇지만, 하나의 제약으로 가질수 있는 것이, assembly name과 xml file의 이름을 동일하게 해줄 필요가 있다. 자동적으로 찾지는 못한다.

Posted by Y2K
,

The Common Language Runtime(CLR) provides a common platform for the execution of programs in .NET.

.NET language is first translated into the Microsoft Intermediate Language(MSIL).

   

Type alias size doesn't match what you're familiar with

Common Language Specification(CLS)의 특징

  • cross-language integration
  • type safety
  • high-performance execution of managed code

WinAPI의 DllImport 시에 Type 값은 서로간에 byte size를 맞춰줄 필요가 있다.

   

Structure and Class differ in behavior

  • class(reference type) : be created on the heap
  • structure(value type) : be created on the stack

   

Returning value types from a method/property is risky

: Value type으로 return을 받을 경우와 Reference type으로 return을 받을 때, 동작을 유의해야지 된다.

DateTime dtDate = DateTime.Now; <- value type

dtDate.AddDate(1) <- dtDate 값은 변화하지 않음.

   

You can't force calls to your value-type constructors

  • value-type constructor에서 일반 생성자를 호출하는 것은 불가능하다.
  • 값의 초기화를 초기값(int :0, boolean : false) 이외에 값으로 초기화 하는 것이 불가능하다.

   

String concatenation is expensive

  • string에서 string의 모음에서 +를 이용하는 것보다, StringBuilder.Append()를 이용해서 string을 모으는 것이 좋다.
  • 1000000번 '.'를 결합할때, +를 사용하는 경우에는 3562.933 sec가 소요되며, StringBuilder를 사용하는 경우에는 0.07sec의 시간이 소요된다.

   

Exceptions may go unhandled

  • Windows app에서는 Application.ThreadException을 handling 시켜주고, Console app에서는 AppDomain.CurrentDomain.UnhandledException을 handling 시켜줘서 사용해준다.
  • 사용자에게 넘겨주는 모든 error를 사용자에게 넘기지 않고, 내부에서 처리가 되어야지 된다.

   

Uninitialized event handlers aren't treated gracefully

Delegate 된 event의 경우에는 연결되지 않을 경우에는 null reference error가 발생한다.

모든 Event를 동작할 때는 반드시 event의 null point error를 check를 해주는 것이 필요하다.

   

Division operation isn't consistent between types

  • 0로 나뉠때 나오는 에러인 DivisionByZeroException이 나올때, double로 나누어질 때는 eps또는 (+-) 무한대로 나타날 수 있기 때문에, DivisionByZeroException이 나타나지 않을 수 있다.
  • 0로 나뉘어지는 것이 우려될 때는 , IsInfinity() method를 호출해서 값의 형태를 확인해봐야지 된다.

   

Typeless ArrayList isn't type-safe

 System.Collections.ArrayList에서는 모든 데이터가 reference type으로 들어가는 것이 가능하지만, 그 값은 type 변환에 안전하지 못하다.

주의해서 사용하거나, System.Generic을 이용해서 type-safe로 만들어주는 것이 좋다.

  

Type.GetType() may not locate all types

선언되어 있는 class library에서 여러개의 중복된 이름을 가질 수 있기 때문에, 하나의 이름만으로 모든 것을 다 얻어내는 것은 불가능하다. 이때에는 reference type name과 version, public key를 모두 적어줘야지 인식시킬 수 있다.

  

Public key reported by sn.exe is inconsistent

"sn.exe -k"로 key를 만들어줄때는 각 key는 private key만이 갖고 있다. public key는 private key에 pair로 만들어서 서명하는 형식으로 만들어줘야지 된다. Visual Studio 2000에서는 "sn.exe -t" 옵션이 잘못 동작하고 있었다. (구버젼을 사용하는 경우에 주의 할것)

Posted by Y2K
,

Windows Mobile에서는 지원하지 않음. 

재미있어보이는 소스라서 들고오기.  

  

메시지 필터를 이용하여 F1, F2, F3 .. 키 입력을 후킹하기.
public class MessageFilter : System.Windows.Forms.IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        switch(m.Msg)
        {
            case 256 : // WM_KEYDOWN     <==== windows message 를 C# 형태로 쓰고 싶었지만.. 딱히 만들어져있는게 없더군요 그래서
                            //                                    인터넷에서 windows.h 파일을 검색해서 찾았습니다. WM_KETDOWN = 256 번이더군요.
                System.Console.WirteLine(m.LParam.ToInt32());        // 이걸로 F1 부터 여러가지 키를 차례데로 눌러본 값들이
                if( m.LParam.ToInt32() == 3866625 ) // F1 Key            // <===  이 값들입니다. 3866625 가 F1 이군요.. 이런식으로 Key 값을 찾아서
                {                                                                            // 계속 추가하면 키보드 후킹.. 그다지 어려운건 아닐거 같군요 ^^;;
                    System.Console.WriteLine("F1 Press");
                    return true;
                }
                else if( m.LParam.ToInt32() == 3932161 )    // F2 Key
                {
                    System.Console.WriteLine("F2 Press");
                    return true;
                }
                else if( m.LParam.ToInt32() == 3997697 )    // F3 Key
                {
                    System.Console.WriteLine("F3 Press");
                    return true;
                }
                break;
        }
        return false;
    }
}
//
//     만들어 놓은 MessageFilter 는 반드시 AddMessageFilter 메서드를 통해 추가해줘야 합니다.
//    static void Main 함수는 기본 Form 에 포함되어 있습니다.  :) 아시죠?
static void Main()
{
    Application.AddMessageFilter(new MessageFilter());
    Application.Run(new JapExamples());
}
아래 있는 것은 windows.h 파일에 정의된 windos message 들입니다. windows.h 파일은 visual studio 안에 INCLUDE_ROOT 에 있습니다.
어딘지는 아시죠?
WA_ACTIVE 1
WA_CLICKACTIVE 2
WA_INACTIVE 0
WM_ACTIVATE 6
WM_ACTIVATEAPP 28
WM_AFXFIRST 864
WM_AFXLAST 895
WM_APP 32768
WM_ASKCBFORMATNAME 780
WM_CANCELJOURNAL 75
WM_CANCELMODE 31
WM_CAPTURECHANGED 533
WM_CHANGECBCHAIN 781
WM_CHAR 258
WM_CHARTOITEM 47
WM_CHILDACTIVATE 34
WM_CLEAR 771
WM_CLOSE 16
WM_COMMAND 273
WM_COMMNOTIFY 68
WM_COMPACTING 65
WM_COMPAREITEM 57
WM_CONTEXTMENU 123
WM_COPY 769
WM_COPYDATA 74
WM_CREATE 1
WM_CTLCOLORBTN 309
WM_CTLCOLORDLG 310
WM_CTLCOLOREDIT 307
WM_CTLCOLORLISTBOX 308
WM_CTLCOLORMSGBOX 306
WM_CTLCOLORSCROLLBAR 311
WM_CTLCOLORSTATIC 312
WM_CUT 768
WM_DEADCHAR 259
WM_DELETEITEM 45
WM_DESTROY 2
WM_DESTROYCLIPBOARD 775
WM_DEVICECHANGE 537
WM_DEVMODECHANGE 27
WM_DISPLAYCHANGE 126
WM_DRAWCLIPBOARD 776
WM_DRAWITEM 43
WM_DROPFILES 563
WM_ENABLE 10
WM_ENDSESSION 22
WM_ENTERIDLE 289
WM_ENTERMENULOOP 529
WM_ENTERSIZEMOVE 561
WM_ERASEBKGND 20
WM_EXITMENULOOP 530
WM_EXITSIZEMOVE 562
WM_FONTCHANGE 29
WM_GETDLGCODE 135
WM_GETFONT 49
WM_GETHOTKEY 51
WM_GETICON 127
WM_GETMINMAXINFO 36
WM_GETOBJECT 61
WM_GETTEXT 13
WM_GETTEXTLENGTH 14
WM_HANDHELDFIRST 856
WM_HANDHELDLAST 863
WM_HELP 83
WM_HOTKEY 786
WM_HSCROLL 276
WM_HSCROLLCLIPBOARD 782
WM_ICONERASEBKGND 39
WM_IME_CHAR 646
WM_IME_COMPOSITION 271
WM_IME_COMPOSITIONFULL 644
WM_IME_CONTROL 643
WM_IME_ENDCOMPOSITION 270
WM_IME_KEYDOWN 656
WM_IME_KEYLAST 271
WM_IME_KEYUP 657
WM_IME_NOTIFY 642
WM_IME_REQUEST 648
WM_IME_SELECT 645
WM_IME_SETCONTEXT 641
WM_IME_STARTCOMPOSITION 269
WM_INITDIALOG 272
WM_INITMENU 278
WM_INITMENUPOPUP 279
WM_INPUTLANGCHANGE 81
WM_INPUTLANGCHANGEREQUEST 80
WM_KEYDOWN 256
WM_KEYFIRST 256
WM_KEYLAST 264
WM_KEYUP 257
WM_KILLFOCUS 8
WM_LBUTTONDBLCLK 515
WM_LBUTTONDOWN 513
WM_LBUTTONUP 514
WM_MBUTTONDBLCLK 521
WM_MBUTTONDOWN 519
WM_MBUTTONUP 520
WM_MDIACTIVATE 546
WM_MDICASCADE 551
WM_MDICREATE 544
WM_MDIDESTROY 545
WM_MDIGETACTIVE 553
WM_MDIICONARRANGE 552
WM_MDIMAXIMIZE 549
WM_MDINEXT 548
WM_MDIREFRESHMENU 564
WM_MDIRESTORE 547
WM_MDISETMENU 560
WM_MDITILE 550
WM_MEASUREITEM 44
WM_MENUCHAR 288
WM_MENUCOMMAND 294
WM_MENUDRAG 291
WM_MENUGETOBJECT 292
WM_MENURBUTTONUP 290
WM_MENUSELECT 287
WM_MOUSEACTIVATE 33
WM_MOUSEFIRST 512
WM_MOUSEHOVER 673
WM_MOUSELEAVE 675
WM_MOUSEMOVE 512
WM_MOUSEWHEEL 522
WM_MOVE 3
WM_MOVING 534
WM_NCACTIVATE 134
WM_NCCALCSIZE 131
WM_NCCREATE 129
WM_NCDESTROY 130
WM_NCHITTEST 132
WM_NCLBUTTONDBLCLK 163
WM_NCLBUTTONDOWN 161
WM_NCLBUTTONUP 162
WM_NCMBUTTONDBLCLK 169
WM_NCMBUTTONDOWN 167
WM_NCMBUTTONUP 168
WM_NCMOUSEMOVE 160
WM_NCPAINT 133
WM_NCRBUTTONDBLCLK 166
WM_NCRBUTTONDOWN 164
WM_NCRBUTTONUP 165
WM_NEXTDLGCTL 40
WM_NEXTMENU 531
WM_NOTIFY 78
WM_NOTIFYFORMAT 85
WM_NULL 0
WM_PAINT 15
WM_PAINTCLIPBOARD 777
WM_PAINTICON 38
WM_PALETTECHANGED 785
WM_PALETTEISCHANGING 784
WM_PARENTNOTIFY 528
WM_PASTE 770
WM_PENWINFIRST 896
WM_PENWINLAST 911
WM_POWER 72
WM_POWERBROADCAST 536
WM_PRINT 791
WM_PRINTCLIENT 792
WM_QUERYDRAGICON 55
WM_QUERYENDSESSION 17
WM_QUERYNEWPALETTE 783
WM_QUERYOPEN 19
WM_QUEUESYNC 35
WM_QUIT 18
WM_RBUTTONDBLCLK 518
WM_RBUTTONDOWN 516
WM_RBUTTONUP 517
WM_RENDERALLFORMATS 774
WM_RENDERFORMAT 773
WM_SETCURSOR 32
WM_SETFOCUS 7
WM_SETFONT 48
WM_SETHOTKEY 50
WM_SETICON 128
WM_SETREDRAW 11
WM_SETTEXT 12
WM_SETTINGCHANGE 26
WM_SHOWWINDOW 24
WM_SIZE 5
WM_SIZECLIPBOARD 779
WM_SIZING 532
WM_SPOOLERSTATUS 42
WM_STYLECHANGED 125
WM_STYLECHANGING 124
WM_SYNCPAINT 136
WM_SYSCHAR 262
WM_SYSCOLORCHANGE 21
WM_SYSCOMMAND 274
WM_SYSDEADCHAR 263
WM_SYSKEYDOWN 260
WM_SYSKEYUP 261
WM_TCARD 82
WM_TIMECHANGE 30
WM_TIMER 275
WM_UNDO 772
WM_UNINITMENUPOPUP 293
WM_USER 1024
WM_USERCHANGED 84
WM_VKEYTOITEM 46
WM_VSCROLL 277
WM_VSCROLLCLIPBOARD 778
WM_WINDOWPOSCHANGED 71
WM_WINDOWPOSCHANGING 70
WM_WININICHANGE 26

출처 : 데브피아(http://www.devpia.com/forum/BoardView.aspx?no=46886&page=2&Tpage=1501&forumname=csharp_qa&stype=&ctType=&answer=) 

Posted by Y2K
,