bad coding practice (FA Team)

ComboBox 패턴

[ bad coding ]

아래 코드는 유진제지 프로젝트에 ComboBoxEx.cs 파일에 작성된 코드

  • ComboBoxEx 클래스, ComboBoxExItems 클래스의 정의가 굳이 필요하지 않음

  • ComboBoxEx 클래스에 findSelectKey, findSelectValue 메서드의 경우 자주 사용되는게 아니라면 정의할 필요가 없고, 자주 사용한다면 [#A03의 예시] 처럼 기능을 조금더 확장시켜 활용 가능

  • GetKey, GetValue 는 메서드로 정의할 필요 없음 [#A04 예시 참고]

public partial class ComboBoxEx : ComboBox
{
    public ComboBoxEx()
    {
        DisplayMember = "DisplayEx";
        ValueMember = "ValueEx";
        this.ForeColor = Color.Black;
    }

    public ComboBoxEx(ComboBoxExItems items)
    {
        this.DisplayMember = "DisplayEx";
        this.ValueMember = "ValueEx";

        this.DataSource = items.getItems();
    }

    public void setDataSource(ComboBoxExItems items)
    {
        this.DataSource = items.getItems();
    }

    public void findSelectKey(string _key)
    {
        foreach (ComboBoxExItem cbitem in this.Items)
        {
            if (cbitem.DisplayEx.Contains(_key))
            {
                this.SelectedItem = cbitem;
                break;
            }
        }
    }

    public bool findSelectValue(string _value)
    {
        foreach (ComboBoxExItem cbitem in this.Items)
        {
            if (cbitem.ValueEx == _value)
            {
                this.SelectedItem = cbitem;
                return true;
            }
        }
        return false;
    }

    public string GetKey()
    {
        if (this.SelectedItem != null)
            return ((ComboBoxExItem)this.SelectedItem).DisplayEx;

        return null;
    }

    public string GetValue()
    {
        if (this.SelectedItem != null)
            return ((ComboBoxExItem)this.SelectedItem).ValueEx;

        return null;
    }
}

public class ComboBoxExItems
{
    List<ComboBoxExItem> items = new List<ComboBoxExItem>();

    public void Add(ComboBoxExItem item)
    {
        items.Add(item);
    }

    public void Clear()
    {
        items.Clear();
    }

    public List<ComboBoxExItem> getItems()
    {
        return items;
    }
}

public class ComboBoxExItem
{
    public string DisplayEx { get; set; }
    public string ValueEx { get; set; }
}

[ good coding ]

#A01 - ComboBox의 Item으로 사용할 자료형 정의

  • 필요에 따라 Property 멤버를 응용할 것

  • System.Windows.Forms.ComboBox 는 기본적으로 Items에 바인딩된 개체의 ToString 메서드를 호출하여 Item의 값을 화면에 표현하므로, Item으로 사용할 자료형에 ToString 메서드를 재정의하여 사용하면 ComboBox.DisplayMember, ComboBox.ValueMember 속성에 매핑이 필요하지 않음

internal class ComboBoxItem
{
    public string Code { get; set; }
    public string Name { get; set; }

    public ComboBoxItem(string code, string name)
    {
        Code = code;
        Name = name;
    }

    public override string ToString()
    {
        return Name;
    }
}

#A02 - Items 초기화

  • 고정된 항목 세팅 (DB로부터 코드,코드명 정보를 읽어오지 않아도 되는 경우)

private void InitComboBoxItems()
{
    var items = new List<ComboBoxItem>();

    // comboBox1 에 대한 처리
    items.Add(new ComboBoxItem(string.Empty, string.Empty)); // 선택하지 않은 빈 값
    items.Add(new ComboBoxItem("A", "에이"));
    items.Add(new ComboBoxItem("B", "비"));
    items.Add(new ComboBoxItem("C", "씨"));
    items.Add(new ComboBoxItem("D", "디"));

    comboBox1.Items.AddRange(items.ToArray());
    comboBox1.SelectedIndex = 0;

    // comboBox2 에 대한 처리
    items.Clear();
    items.Add(new ComboBoxItem(string.Empty, string.Empty));
    items.Add(new ComboBoxItem("100", "코드100"));
    items.Add(new ComboBoxItem("200", "코드200"));

    comboBox2.Items.AddRange(items.ToArray());
    comboBox2.SelectedIndex = 0;
}
  • DB에 저장된 코드,코드명 정보를 읽어와 항목 세팅

private void InitComboBoxItems()
{
    DataSet result = SqlHelper.ExecuteSql("SELECT user_id, user_name FROM sysUserMaster WHERE rtrchk = 'N'");
    DataTable resultTable = result?.Tables?.Cast<DataTable>()?.ElementAtOrDefault(0);

    var items = new List<ComboBoxItem>();
    items.Add(new ComboBoxItem(string.Empty, string.Empty)); // 선택하지 않은 빈 값

    // 쿼리 결과가 있을때만 Item 세팅
    if (resultTable != null)
    {
        foreach (DataRow row in resultTable.Rows)
        {
            string userId = row.GetValue("user_id")?.ToString() ?? string.Empty;
            string userName = row.GetValue("user_name")?.ToString() ?? string.Empty;
            items.Add(new ComboBoxItem(userId, userName));
        }
    }

    comboBox1.Items.AddRange(items.ToArray());
    comboBox1.SelectedIndex = 0;
}

#A03 - ComboBox에서 Item 찾기

internal static class InternalUtil
{
    static bool FindComboItemByName(this ComboBox comboBox, string name, out string code)
    {
        var comboItem = comboBox.Items.Cast<ComboBoxItem>().FirstOrDefault(x => x.Name.Equals(name));

        code = comboItem?.Code;

        return comboItem != null;
    }

    static bool FindComboItemByKey(this ComboBox comboBox, string key, out string name)
    {
        var comboItem = comboBox.Items.Cast<ComboBoxItem>().FirstOrDefault(x => x.Name.Equals(key));

        name = comboItem?.Name;

        return comboItem != null;
    }
}

#A04 - ComboBox에서 선택된 Item을 캐스팅하여 특정 멤버 참조

// ComboBox에 선택된 아이템의 Code 가져오기
string code = (comboBox1.SelectedItem as ComboBoxItem)?.Code;

// ComboBox에 선택된 아이템의 Name 가져오기
string name = (comboBox1.SelectedItem as ComboBoxItem)?.Name;

#B01 - Form.ShowDialog / 팝업을 통한 값 세팅

아래 코드는 유진제지 프로젝트에 불량 정보를 입력받는 폼(badqty class)에서 불량 유형을 선택하기 위해 불량 유형 폼(bad_NM class)으로부터 값을 처리위해 작성된 코드의 일부

객체지향에서 일반적으로 어떤 객체가 다른 객체를 참조하여 멤버를 직접 업데이트하는 방식은 좋은 코딩이 아님

[ bad coding ]

  • 솔루션팀에서 개발한 거의 모든 코드에 Form 간 데이터 참조 방식이 생성자에서 부모 폼의 객체를 전달하는 방식으로 이루어져 있음

  • 실행하려는 팝업 폼을 클래스의 필드 멤버로 구성할 이유가 없음 (예시의 badqty 클래스에 private bad_NM bad_NM; )

  • 캡슐화 위반 : bad_NM 클래스에서 생성자를 통해 전달받은 badqty Home 객체의 lbl_Bad_NM 멤버와 lbl_bad_code 멤버의 값을 수정하기 위해 public 으로 접근 제한자 지정 (badqty.Designer.cs 참고)

  • DataGridView.CellClick 이벤트가 발생하는 시점에 lbl_Bad_NMlbl_bad_code의 Text 속성에 값을 변경하고, button4_Click_1 이벤트 핸들러에서 불량 유형 선택 판단 조건에 lbl_Bad_NM.Text에 값 할당 여부를 확인하는 방식은 lbl_Bad_NM.Text 속성에 값이 언제 변경되는지를 확인해야 하므로 직관적이지 못함

[ good coding ]

  • 불량 정보 입력 팝업(BadInfoSetPopup class)에서 불량 코드를 세팅하기 위한 팝업(BadCodePopup class)의 객체를 버튼이 클릭되는 시점에 생성하며, BadCodeBadCodeName 멤버의 값을 참조하여 처리

  • DataGridView 컨트롤에 항목 초기화 (InitGridViewItem 메서드)

  • 불량 유형 선택된 여부는 DataGridView.CurrentRow 속성으로 판단

/********** badqty.cs **********/
public partial class badqty : Form
{
    MainForm Home;
    private bad_NM bad_NM;

    public badqty(MainForm home)
    {
        InitializeComponent();
        this.Home = home;
    }
    
    // ...
    
    private void btn_bad_Click_1(object sender, EventArgs e)
    {
        bad_NM = new bad_NM(this);

        bad_NM.ShowDialog();
    }
}

/********** badqty.Designer.cs **********/
partial class badqty
{
    //...
    public System.Windows.Forms.Label bad_code;
    public System.Windows.Forms.Button btn_bad;    
    //...
}


/********** bad_NM.cs **********/
public partial class bad_NM : Form
{
    badqty Home;

    public bad_NM(badqty home)
    {
        InitializeComponent();
        Home = home;
    }
    
    private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        lbl_Bad_NM.Text = dataGridView1.CurrentRow.Cells["col_bad_type"].Value.ToString().Trim();
        lbl_bad_code.Text = dataGridView1.CurrentRow.Cells["col_bad_code"].Value.ToString().Trim();
    }
    
    private void button4_Click_1(object sender, EventArgs e)
    {
        if (lbl_Bad_NM.Text != " " || lbl_Bad_NM != null)
        {
            Home.btn_bad.Text = lbl_Bad_NM.Text.ToString().Trim();
            Home.bad_code.Text = lbl_bad_code.Text.ToString().Trim();
            this.Close();
        }
        else
        {
            MessageBox.Show("불량유형을 터치해주세요.", "경고", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
    }
}
        
        

#C01 - Timer

[ bad coding ]

public partial class Main : Form
{
    System.Threading.Timer Main_Timer;
    System.Threading.Timer Run_Timer;
    System.Threading.Timer Main_Load_Timer;
    
    public Main()
    {
        InitializeComponent();

        Main_Timer = new System.Threading.Timer(Main_Callback);
        Run_Timer = new System.Threading.Timer(Run_Callback);
        Main_Load_Timer = new System.Threading.Timer(Main_Load_Callback);
    }
    
    void Main_Callback(object s)
    {
        작업경과시간 = stopwatch.Elapsed.Add(time).ToString().Split('.')[0];
        SetLabel(lb_작업시간, 작업경과시간);

        SetLabel(lb_생산수량, 생산수량.ToString());
    }

    void Main_Load_Callback(object s)
    {
        digitalDisplayControl1.DigitText = DateTime.Now.ToString("HH:mm:ss");
    }

    void Run_Callback(object s)
    {
        DB데이터업로드();
    }
}

#D01 - 프로그램이 실행중인지 판단하여 중복 실행 제어

  • Form이 Load 되기 전에 처리하며, 자신의 프로세스ID를 제외한 같은 이름의 프로그램이 실행 중인지를 판단하여 실행된 응용 프로그램을 종료

private void MainForm_Load(object sender, EventArgs e)
{
    Process[] running_process = Process.GetProcessesByName("GST_IOT");
    
    if(running_process.Length > 1)
    {
        Messagebox = new POPUP.MessageBox("프로그램이 이미 실행중입니다.", true);
        Messagebox.ShowDialog();
        Close();
    }
    else
    {
        //...
    }
}

etc.

Last updated

Was this helpful?