본문 바로가기

Refactoring

.net Winform 에서 코드 룩업하기 리펙토링 step2

Step 2

 Step1 문제점

 단순히 콤보박스에 코드데이터를 바인딩하는 작업은 가능하지만, 다른 화면에서 다른 코드를 바인딩하고 싶을 경우 비슷한 내용의 이벤트 코드를 다시 작성해야된다. 프로그래밍에 있어서 비슷한 코드를 복사해서 붙여넣기 하는 경우는 가급적이면 하지 말아야 하는 행위이다. 코드를 복사해서 붙여넣을 경우 기존코드가 문제가 되거나 기능이 추가 변경될 경우에도 복사해서 붙여넣은 곳에 모두 변경을 해야 되는 상황이 온다. 더불어 상당히 많은 작업이 초례되고 그 작업이 다시 버그를 불러일으킬 가능성이 높아진다.

 Step1 해결

  우선적으로 콤보박스에 진입이 일어나면 코드를 바인딩 하는 부분을 다른 화면에서도 재사용할 수 있도록 별도의 클래스로 빼어내도록 해보겠다.

 코드의 바인딩을 담당하는 클래스

public class ComboBoxCodeBinder
{
    private ComboBox comboBox;
    private IEnumerable<CodeData> codeDatas;
    public ComboBoxCodeBinder(ComboBox comboBox, IEnumerable<CodeData> codeDatas)
    {
        this.comboBox  = comboBox;
        this.codeDatas = codeDatas;

        this.comboBox.Enter += new EventHandler(Event_Enter);
    }

    private void Event_Enter(object sender, EventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;

        comboBox.DataSource = new BindingSource()
        {
            DataSource = this.codeDatas.Select(x => x)
        };

        comboBox.ValueMember   = "Code";
        comboBox.DisplayMember = "Caption";
    }
}
public partial class Form1 : Form
{
    private List<CodeData> codeDatas = new List<CodeData>();
    private ComboBoxCodeBinder comboBoxCodeBinder;
    public Form1()
    {
        InitializeComponent();

    }
    private void Form1_Load(object sender, EventArgs e)
    {
        codeDatas.Add(new CodeData() { CodeDiv = "01", Code = "01.01", Caption = "매입" });
        codeDatas.Add(new CodeData() { CodeDiv = "01", Code = "01.02", Caption = "매출" });
        codeDatas.Add(new CodeData() { CodeDiv = "01", Code = "01.03", Caption = "기타" });

        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.01", RefCodeDiv = "01.01", Caption = "계약매입" });
        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.02", RefCodeDiv = "01.01", Caption = "현장매입" });
        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.03", RefCodeDiv = "01.02", Caption = "본사매출" });
        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.04", RefCodeDiv = "01.02", Caption = "현장매출" });
        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.05", RefCodeDiv = "01.03", Caption = "기타매입" });
        codeDatas.Add(new CodeData() { CodeDiv = "02", Code = "02.06", RefCodeDiv = "01.03", Caption = "기타매출" });

        comboBox1.DisplayMember = "Caption";
        comboBox1.ValueMember   = "Code";
        comboBox2.DisplayMember = "Caption";
        comboBox2.ValueMember   = "Code";

        this.comboBoxCodeBinder = new ComboBoxCodeBinder(comboBox1, codeDatas.Where(x => x.CodeDiv.Equals("01")).Select(x => x));

        comboBox2.Enter += new EventHandler(Event_EnterComboBox2);

        button1.Click   += new EventHandler(Event_ClickButton);
    }
    private string GetComoboBoxValue(ComboBox comboBox)
    {
        string value = string.Empty;

        object selectedObject = comboBox.SelectedItem;

        if (selectedObject != null)
            value = ((CodeData)comboBox.SelectedItem).Code;

        return value;
    }
    private void Event_ClickButton(object sender, EventArgs e)
    {
        string combo1Value = GetComoboBoxValue(comboBox1);
        string combo2Value = GetComoboBoxValue(comboBox2);

        //something...
    }
    private void Event_EnterComboBox2(object sender, EventArgs e)
    {
        comboBox2.DataSource = new BindingSource()
        {
            DataSource = codeDatas.Where(x => x.CodeDiv.Equals("02") && x.RefCodeDiv.Equals(GetComoboBoxValue(comboBox1))).Select(x => x)
        };
    }
}

 다른 값과 연계가 없는 '구분1'의 코드를 바인딩 하는 클래스는 독립적으로 잘 작동한다. 하지만 여러가지 소스들이 아직은 갈길이 먼것과 같은 모습을 보여준다.

 왜? 상속을 통한 사용자 컨트롤 확장을 하지 않는가?

 ComboBox 컨트롤을 상속받아서 코드를 바인딩 하는 사용자컨트롤을 만들 수도 있다. 객체지향을 처음 공부하는 초기에는 사용자 컨트롤을 만드는 것을 주저 하지 않았다. 하지만 특정 한두개의 기능을 확장하여 컨트롤을 만들어 드롭다운으로 쉽게 디자인하고 적용할 때까지만 해도 앞으로 닥칠 시련이 무엇인지 몰랐다. 그 시련이 무엇인지 깨닭는데는 불과 몇주도 걸리지 않았다.

 시간이 조금 지나서 비슷한 기능이 필요한 UI 클래스가 필요하게 되니 또 사용자 컨트롤을 만들어야 했다. 그런식으로 사용자 컨트롤이 닷넷에서 제공하는 컨트롤의 갯수의 곱으로 늘어나기 시작했다. 그리고 만들고 다시 개발툴에 적용해야되는 수고도 지속적으로 이루어지고 있었다. 이쯤 되어서 무엇인가 잘 못되었다는 것을 깨닭았다.

 그 뒤로는 사용자 컨트롤은 내 프로젝트에서 매우 제한적인 상황에서만 적용을 한다. 특히 UI컨트롤이 존재하거나 소스가 공개된 오픈소스의 컨트롤 혹은 클래스의 어떤 기능을 추가하거나 확장할 때도 함부로 상속이나 때로는 실제 원본소스의 특정기능을 수정하는 등의 행위를 하지 않는다.

 가급적이면 비침투적 요법(상대의 코드를 변경하지 않고 랩핑하므로서 그 기능을 확장)으로 클래스의 기능을 확장하거나 나에게 필요한 별도의 클래스로 확장해 나가는 방식을 취하고 있다. 그렇지 않으면 지속적인 버전업과 변경에 대해서 대응이 불가능해지는 헬게이트를 여는 날이 온다는 것을 경험을 통해서 배웠기 때문이다.