티스토리 뷰

목차

    반응형

    디지인 패턴 중 상태패턴 (C# state pattern)을 활용한 응용 예제입니다. 실행 화면은 아래와 같습니다.


    c# 스테이트 패턴 프로그램[디자인 패턴 예제 C#] 실행 화면


    State Pattern 프로젝트 - TestImageButton(State).zip

    스테이트 패턴 실행파일 TestImageButton(State)exe.zip


    전체 소스는 바로 위 링크를 참조하세요.


    화면에서 위로 버튼을 누르면, 12가 증가하고, 아래로 버튼을 누르면, 12가 감소합니다. C# state pattern 상태 패턴 예제 소스 설명 시작합니다.



    c# State Pattern 구현할 기능이 담긴 인터페이스를 하나 선언합니다.



    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    namespace TestImageButton_State_
    {
        interface IState
        {
            // 그림이랑 값을 바꾸는거 두 개 밖에 없음
            // 추가하고 싶은 상태는 여기에 미리 정의
            void ChangePicture();
            void ChangeTestValue(int isIncrease);
        }
    }
    cs



    이어서, c# 스테이트 패턴의 State를 제어할 컨트롤 클래스를 하나 선언합니다.



    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
    80
    81
    82
    83
    84
    85
    86
    87
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    using System.Windows.Forms;
    using System.Drawing;
     
    namespace TestImageButton_State_
    {
        class State
        {
            // 0~9까지의 버튼, 폼의 100, 10, 1자리 픽쳐 박스
            // 픽쳐 박스의 경우엔 박싱/언박싱 문제가 있을 수 있음
            public ImageList ilNo;
            public PictureBox[] pbValue = new PictureBox[3];
     
            IState state;
            IState nullState;
            IState valueChange;
     
            private int _controlValue = 500;
     
            public int controlValue
            {
                get { return _controlValue; }
                set { _controlValue = value; }
            }
     
            public int StateOfObject
            {
                set
                {
                    if (value == 0) state = nullState;
                    else if (value == 1) state = valueChange;
                    else state = nullState;
                }
            }
     
            public State()
            {
                nullState = new NullState(this);
                valueChange = new ValueChange(this);
     
                ilNo = new ImageList();
                ilNo.ImageSize = new Size(3345);
                ilNo.Images.Add(Properties.Resources.no0);
                ilNo.Images.Add(Properties.Resources.no1);
                ilNo.Images.Add(Properties.Resources.no2);
                ilNo.Images.Add(Properties.Resources.no3);
                ilNo.Images.Add(Properties.Resources.no4);
                ilNo.Images.Add(Properties.Resources.no5);
                ilNo.Images.Add(Properties.Resources.no6);
                ilNo.Images.Add(Properties.Resources.no7);
                ilNo.Images.Add(Properties.Resources.no8);
                ilNo.Images.Add(Properties.Resources.no9);
            }
     
            public void ReleaseMemory()
            {
                // 이미지 리스트 메모리 해제
                for (int ilCount = 0; ilCount < ilNo.Images.Count; ilCount++)
                {
                    ilNo.Images[ilCount].Dispose();
                }
     
                ilNo.Dispose();
     
                // 픽쳐 박스 메모리 해제
                pbValue[0].Dispose();
                pbValue[1].Dispose();
                pbValue[2].Dispose();
     
                GC.Collect();
            }
     
            public void ChangePicture()
            {
                state.ChangePicture();
            }
     
            public void ChangeTestValue(int isIncrease)
            {
                state.ChangeTestValue(isIncrease);
            }
        }
    }
    cs



    그리고, 각 상태에 따른 c# State Pattern 동작을 구현해야합니다.

    스테이트 패턴의 특징이 바로 구현부를 따로 떼어낸다는 것입니다.


    아래는 스테이트 상태가 Null일 경우입니다.



    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
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    using System.Windows.Forms;
     
    namespace TestImageButton_State_
    {
        class NullState : IState
        {
            State st;
     
            public NullState(State _st)
            {
                this.st = _st;
            }
     
            public void ChangePicture()
            {
                MessageBox.Show("그림 못바꿔요");
            }
     
            public void ChangeTestValue(int isIncrease)
            {
                MessageBox.Show("값도 못바꿔요");
            }
        }
    }
    cs



    다음은, 스테이트 패턴에서 실제로 값을 변경하는 경우입니다.


    상속받은 State Pattern 인터페이스를 먼저 정의합니다. ValueChange() 함수에서 조작해야 할 State를 상황에 맞게 변경해 주죠.


    ChangePicture() 함수가 호출된다면, 100 자리의 수를 1자리 씩 끊어 별도의 변수에 저장합니다. 세 자리를 한꺼번에 다루는 상황이 아니기에 부득이 저렇게 해야 합니다.


    계산이 끝나면, 일단 픽처 박스의 이미지를 비웁니다.

    Dispose() 함수를 이용해서요.


    이 소스를 생략하고 바로 새 이미지로 덮어씌워도 되는데, 이러면 메모리 누수가 발생합니다. 개발자는 이미지 교체를 생각했지만, 컴파일러 입장에선 이미지 위에 다른 이미지를 올리는 것이니깐요.


    이점만 주의하면 됩니다.



    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
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
     
    namespace TestImageButton_State_
    {
        class ValueChange : IState
        {
            State st;
     
            public ValueChange(State _st)
            {
                this.st = _st;
            }
     
            public void ChangePicture()
            {
                int hundred, ten, one, toBeChange;
                toBeChange = st.controlValue;
     
                hundred = Convert.ToInt32(Math.Truncate(toBeChange * 0.01));
                ten = Convert.ToInt32(Math.Truncate((toBeChange - (hundred * 100)) * 0.1));
                one = Convert.ToInt32(toBeChange - (hundred * 100- (ten * 10));
     
                // 각각의 이미지에 대한 Dispose를 해주지 않으면 메모리 좔좔 샘
                // 가비지 컬렉터는 완벽하지 않음
                st.pbValue[0].Image.Dispose();
                st.pbValue[1].Image.Dispose();
                st.pbValue[2].Image.Dispose();
     
                st.pbValue[0].Image = st.ilNo.Images[hundred];
                st.pbValue[1].Image = st.ilNo.Images[ten];
                st.pbValue[2].Image = st.ilNo.Images[one];
     
                GC.Collect();
            }
     
            public void ChangeTestValue(int tmpRefValue)
            {
                int tmp = 12 * tmpRefValue;
                st.controlValue += tmp;
                st.controlValue = (st.controlValue > 999) ? 999 : st.controlValue;
                st.controlValue = (st.controlValue < 100) ? 100 : st.controlValue;
            }
        }
    }
    cs



    마지막으론, c# State Pattern 폼의 코드입니다.


    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
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
     
    namespace TestImageButton_State_
    {
        public partial class Form1 : Form
        {
            State stateController;
     
            readonly int stateIsNull = 0;
            readonly int stateIsChange = 1;
            PictureBox[] pbValue = new PictureBox[3];
     
            public Form1()
            {
                InitializeComponent();
     
                pbValue = new PictureBox[3];
                pbValue[0= pbCount1;
                pbValue[1= pbCount2;
                pbValue[2= pbCount3;
     
                stateController = new State
                {
                    pbValue = this.pbValue
                };
     
                // 처음엔 state 상태를 Null로
                stateController.StateOfObject = stateIsNull;
            }
     
            private void pbControl_MouseUp(object sender, MouseEventArgs e)
            {
                string targetName = ((PictureBox)sender).Name.ToString();
     
                // 마우스를 뗐으니 평상시 버튼으로 변경
                if (targetName.CompareTo("pbUp"== 0
                {
                    pbUp.Image.Dispose();
                    pbUp.Image = Properties.Resources.SubForm___Home___ArrowUp;
                }
     
                if (targetName.CompareTo("pbDown"== 0)
                {
                    pbDown.Image.Dispose();
                    pbDown.Image = Properties.Resources.SubForm___Home___ArrowDown;
                }
     
                GC.Collect();
     
     
                // 마우스를 뗐으니 왼쪽에 값 변경
                stateController.ChangePicture();
            }
     
            private void pbControl_MouseDown(object sender, MouseEventArgs e)
            {
                string targetName = ((PictureBox)sender).Name.ToString();
     
                // 선택된 버튼으로 교체
                if (targetName.CompareTo("pbUp"== 0)
                {
                    pbUp.Image.Dispose();
                    pbUp.Image = Properties.Resources.SubForm___Home___ArrowUp_sel;
                }
     
                if (targetName.CompareTo("pbDown"== 0)
                {
                    pbDown.Image.Dispose();
                    pbDown.Image = Properties.Resources.SubForm___Home___ArrowDown_sel;
                }
     
                GC.Collect();
     
                
                // state 설정
                stateController.StateOfObject = stateIsChange;
     
                // + 또는 - 설정
                int tmpRefValue = (targetName.CompareTo("pbUp")) == 0 ? 1 : -1;
                stateController.ChangeTestValue(tmpRefValue);
            }
     
            private void CreatePictureBox(string pbName)
            {
     
            }
     
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                stateController.ReleaseMemory();
     
                pbValue[0].Dispose();
                pbValue[1].Dispose();
                pbValue[2].Dispose();
     
                GC.Collect();
            }
        }
    }
    cs



    pbControl_MouseUp() 함수와 pbControl_MouseDown() 함수는 화살표가 갖는 이벤트 함수가 되겠습니다.


    종종, C#의 가비지 컬렉터를 지나치게 신뢰한 나머지, 메모리 해제의 필요성에 대해 아예 생각지도 않은채 넘어가는 경우가 많은데, 메모리 해제는 꼭 해줘야 합니다. 가비지 컬렉터는 똑똑할지언정 완벽한 존재는 아닙니다.


    이것으로 C# 스테이트 패턴 예제 소개를 마칩니다.




    State Pattern 프로젝트 - TestImageButton(State).zip

    스테이트 패턴 실행파일 TestImageButton(State)exe.zip


    ps. 이미지는 절대 경로로 하세요. 리소스에서 불러오는 것보다 훨씬 빨라요.



    관련 글


    [C# 디자인 패턴] 팩토리 메소드 패턴 예제 (Factory Pattern)


    C# 팩토리 패턴 예제 (추상 Abstract Factory Method Pattern)


    C++ 싱글톤 패턴 + Friend 키워드 예제, 디자인패턴 사용 방법



    ⓒ written by vicddory

    반응형