본문 바로가기

Programing(프로그래밍)

c# 도형그리기와 팩토리 패턴(객체지향OOP, 클래스cs 관리, 프로퍼티 등)


0.

이번 코딩은 처음 도형그리기를 만들고 나서, 도형을 상속받는식으로 또 고친 후, 팩토리 패턴으로 다시 만들었다.

그리고 OOP[각주:1]라는걸 몇번 들어보기도 했고, 중요하다는 이야기도 들었지만 그동안 확실하게 개념이 잡혀있던게 아니었던지라

처음 이번 코드를 OOP로 작성하면서 확실하게 개념을 잡아보자 라는 생각으로 진행했다.

참고로 OOP의 기본 원칙은 추상화(Abstraction), 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism)이다.


1.

보통 VS에서 처음 프로젝트랑 파일을 만들면 클래스(Program라는 이름의...)를 하나 자동으로 만들어주는데 난 그동안 이게 뭔지 몰랐다...ㄷㄷ

이번에 다른 클래스들을 만들어보면서 클래스라는게 사실 일종의 도안(청사진)같은거고, 

기본적으로 그 클래스 안의 함수들을 사용하려면 클래스를 기반으로하는 객체를 만들어줘야 사용가능하다는걸 알았다.

static을 쓰는 이유도 몰랐는데, 

static을 클래스 안의 함수앞에 붙여주면 후에 생성해줄 객체들이 그 함수를 가져가서 작동하는게 아니라

클래스가 그 함수를 가지게 되어 객체를 만들지 않아도 클래스를 바로 불러서 직접 사용이 가능하다.

main 클래스가 static인건 위의 이유에서다.


2.

그리고 한 클래스당, 한 개의 파일을 두는게 일반적이다.

이게 어찌보면 캡슐화의 한 종류인듯하다. A클래스에서 B클래스로 접근이 자유로우면 정보은닉이 힘드므로...

(Enum(열거형식) 도 마찬가지)


3.

클래스들은 나중에 객체가 될 정보(변수나 함수)들을 가지고 있다.

클래스가 연필 도안이라면 객체는 연필이다.

연필 도안에는 연필 크기(변수)가 얼만큼일지, 연필 색(변수)은 무엇인지, 연필이 수행해야 할 기능(함수)등이 들어있다.

이게 추상화다. 일반적으로 작동하는 것들을 프로그래밍화 시키는것.


4.

근데 만약에 후에 내가 지우개달린 연필을 만들고 싶어졌다면? 기존에 있는 클래스(도안)을 수정할것인가?

물론 수정해도 되겠지만 그럼 다시는 지우개가 없는 연필을 만들긴 어려울것이다.

이 때 사용하는게 상속. ( class 내클래스 : 부모클래스 이런식으로 쓴다)

지우개연필 클래스에, 연필 클래스(도안)을 상속받으면 내가 따로 정해주지 않아도 

이미 연필클래스의 크기나 기능은 지우개연필 클래스 안에 모두 가져와진다.


5.

근데 또 만약에 다시 연필 색을 빨간색으로 바꾸고 싶어졌다. 기존 클래스를 수정하면 다시는 검은 연필을 만들수가 없다.

그럼 어떻게 할까?

원래 연필 클래스로 돌아가서 "나중에 바꿀수 있게 해줘"라고 표시해준 후,

"바꿀 수 있게 된"연필 클래스를 상속받아 색깔 정보를 "덮어씌워서" 색연필 클래스를 만들면 된다.

여기 쓰이는 키워드가 (바꿀 수 있게)virtual과 (덮어씌우는)override다. 이게 다형성.


6.

내코드에서 다른 도형클래스들이 상속받은 클래스(shape)는 객체 생성을 해도 할수있는게 없다. 

그래서 아예 처음부터 순수가상클래스(absract)로 만들었다. 가상클래스는 객체 생성이 불가능하다. 그리고 후에 override가 가능하다.


7.

근데 아무 기능도 없는데 왜 상속을 받냐고? 

상속을 받으면 부모클래스의 하위 타입(Type)이 되어서 나중에 한꺼번에 관리하기가 편하다.

triangle과 circle은 서로 다른 클래스고 객체가 만들어져도 각각 triangle타입, circle타입이라 관리하기가 복잡한데,

shape을 상속받게되면 triangle 객체도 shape, circle 객체도 shape 타입으로 관리가 가능해진다.


8.

그리고 "팩토리 패턴"은 사실 OOP랑은 좀 거리가 있는 디자인 패턴의 한 종류라고 하는데,

어쨌든, 연필 만들고 싶을 때 마다 매번 사용자가 만들어주는게 아니라, 찍어내는 공장을 만들어서

원할때마다 그 공장을 가동시키기만 하면 연필이 나오는(...) 걸 만드는게 목적이라고 할 수 있겠다. 


이번 코드에서는 static을 쓰지 않고 팩토리를 만들었는데, 굳이 공장이 여러개일 필요가 없다면

객체들(공장들)을 따로 만들지 않고 static으로 코드를 짜도 무방하겠다.(하지만 여러개가 될 수도 있지!!캬캬)


길게도 적었네...

여기까지가 이번 코딩에서 배운점이고.(빼먹은게 있을 수 있음 ㅠㅠ)

몇몇 참고로 생각나는 부분은 각주로.


아래는 이제 코드들.


코드를 간략히 설명하면, 

1. 5개의 입력을 차례로 받은 후,

2. 도형을 그려주는 팩토리에게 넘긴 후

3. 다섯번 팩토리가 도형을 골라서 만들어 저장한 후

4. 그려주자.



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

using System;
 
namespace drawshapefactory
{
    class Program
    {
 
        static void PrintInstruction(ShapeType shape)
        {
            Console.WriteLine(shape.ToString() + ": " + (int)shape);
        }
        static void Main(string[] args)
        {
            PrintInstruction(ShapeType.Triangle);
            PrintInstruction(ShapeType.Circle);
            PrintInstruction(ShapeType.Rectangle);
            PrintInstruction(ShapeType.Square);
 
            Shape[] inputShape = new Shape[5];
 
            int length = inputShape.Length;
            ShapeFactory shapefactory = new ShapeFactory();    //
 
            for (int i=0; i < length; i++)
            {
                Console.Write("Please input number :");
                int inputnumber = Int32.Parse(Console.ReadLine());
                inputShape[i] = shapefactory.CreateShape((ShapeType)inputnumber);
            }
 
            foreach (Shape i in inputShape)
            {
                i.Draw();
            }
        }
    }
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
 
namespace drawshapefactory
{
    public enum ShapeType [각주:2]
    {
        Triangle,
        Circle,
        Rectangle,
        Square
    };
}
 
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
namespace drawshapefactory
{
    public abstract class Shape
    {
        protected int Size { get; private set; }[각주:3]
 
        public Shape(int size)
        {
            Size = size;
        }
        public virtual void Draw()
        {}
    }
}
 
cs


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;
 
namespace drawshapefactory
{
    class Triangle : Shape
    {
        public Triangle(int size) : base(size)
        {}
 
        public override void Draw()
        {
            for (int i = 1; i < Size; i++)
            {
                for (int k = Size/2; k > i / 2; k--)
                {
                    Console.Write(" ");
                }
                for (int j = i; j > 0; j--)
                {
                    Console.Write("#");
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }
}
 
cs
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
 
using System;
 
namespace drawshapefactory
{
    class Rectangle : Shape
    {
        protected int Height { get; private set; }
 
        public Rectangle(int size, int height) : base(size)
        {
            Height = height;
        }
        
        public override void Draw()
        {
            base.Draw();
 
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    Console.Write("#");
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }
}
 
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
namespace drawshapefactory
{
    class Square : Rectangle
    {
        public Square(int size) : base(size)
        {}
 
        public override void Draw()
        {
            base.Draw();
        }
    }
}
 
cs
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
 
using System;
 
namespace drawshapefactory
{
    class Circle : Shape
    {
        public Circle(int size) : base(size)
        {}
 
        public override void Draw()
        {
            base.Draw();
 
            int radial = Size / 2;      
            for (int i = 0; i < Size; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    if (radial * radial > (i - radial) * (i - radial) + (j - radial) * (j - radial))
                    {
                        Console.Write("#");
                    }
                    else
                    {
                        Console.Write(" ");
                    }
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }
}
 
cs
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
namespace drawshapefactory
{
    public class ShapeFactory
    {
        public Shape CreateShape(ShapeType s)
        {
            const int size = 16;                 //basic size.
            const int rectangleheight = 10;      //
 
            switch (s)
            {
                case ShapeType.Triangle:
                    return new Triangle(size);
 
                case ShapeType.Circle:
                    return new Circle(size);
 
                case ShapeType.Rectangle:
                    return new Rectangle(size, rectangleheight);
 
                case ShapeType.Square:
                    return new Square(size);
 
                default:
                    return null;
            }
        }
    }
}
cs

아 이제 직원 관리 시스템 코딩 한것도 분석하러가야됨 ㅠㅠ 헉헉 바쁘다 바빠.

근데 왜 티스토리 에디터에는 consola 폰트가 없지?



  1. OOP 한글위키 : https://goo.gl/pYKp6i [본문으로]
  2. Enum : https://msdn.microsoft.com/ko-kr/library/sbbt4032(v=vs.120).aspx [본문으로]
  3. 프로퍼티 : http://mrw0119.tistory.com/15 [본문으로]