C#

객체 지향 프로그래밍 (OOP)

temp-franc 2023. 8. 14. 17:00

객체 지향 프로그래밍 (Object-Oriented Programming)

 : 컴퓨터 프로그램을 여러 명령어의 목록으로 보지 않고 여러 개의 독립된 객체들의 집합으로 파악하는 프로그래밍 기법.

 

 I. 객체, 클래스, 인스턴스

  A. 객체 (object)

  : 객체 지향 프로그래밍(OOP) 에서 클래스(class)가 실제로 구현된 인스턴스(instance)

    혹은 클래스(class)에서 구현하기 위한 추상적인 개념

 

  : 프로그램에서 사용되는 데이터 혹은 식별자에 의해 참조되는 공간

 

 

   ex) 고양이라는 개념

 

  B. 클래스 (class)

   : 객체 지향 프로그래밍(OOP)에서 특정 객체를 생성하기 위해 변수와 메소드를 정의하는 템플릿

   : 클래스에서는 해당 객체의 속성(state)는 변수(variable)로, 기능(behavior)은 메서드(method)로 정의함.

    ex) 고양이라는 객체를 프로그램상 나타내기 위해 변수와 메서드를 정의

    internal class Cat
    {
        // 속성 정의
        public string catName;                                 // 이름은 뭔지
        public string catSpecies;                              // 묘종은 뭔지..
        public string color;                                   // 색은 뭔지..
        public int catAge;                                     // 나이는 몇 살인지..

        // 기능 정의
        public void meow()                                    // 야옹하고 울기
        {
            Console.WriteLine("Meow!");
        }

        public void moving()
        {
            Console.WriteLine(catName + " is moving...");      // 이동하기
        }


        public void jump()
        {
            Console.WriteLine("...Jump!");                      // 점프하기
        }
    }

 

  C. 인스턴스 (instance)

   : 객체 지향 프로그래밍(OOP)에서 클래스에 소속된 개별적인 객체.

 

   : 정의된 클래스에 객체를 생성한 경우, 그 객체는 해당 클래스의 인스턴스가 된다.

   : 하나의 클래스를 사용하여 유사한 성질을 가진 수많은 인스턴스를 생성할 수 있다.

 

     ex) 고양이 "냬옹스(Nyeaons)"는 Cat 클래스의 인스턴스

Nyeaons

    internal class Program
    {
        static void Main(string[] args)
        {
            Cat Nyeaons = new Cat();
            Nyeaons.catName = "Nyeaons";                     // 고양이의 이름은 냬옹스다.
            Nyeaons.catSpecies = "스코티시폴드";             // 묘종은 스코티시 폴드.
            Nyeaons.color = "티끌없는 화이트";               // 색깔은 티끌없는 화이트.
            Nyeaons.catAge = 5;                              // 나이는 5살.

            Console.WriteLine($"이 고양이의 이름은 {Nyeaons.catName}다. 묘종은 {Nyeaons.catSpecies}이며, {Nyeaons.color}색을 지닌 귀여운 고양이지");
            Console.WriteLine($"나이는 {Nyeaons.catAge.ToString()}살 이다");
            
            
            Console.WriteLine($"{Nyeaons.catName} 물어!");
            Nyeaons.moving();
            Nyeaons.jump();
            Nyeaons.meow();

        }
    }

>

  ** 객체의 정의를 보면, "클래스(class)가 실제로 구현된 인스턴스(instance)". 즉, Nyeaons는 인스턴스이자 객체이다.

      개념의 혼동을 피하기 위해 chatGPT에 물어보았다..

 

II. OOP의 4가지 속성

  A. 추상화 (Abstraction)

 : 프로그램으로 표현하기 위하여, 실제의 개념을 단순화하고 필요한 부분을 강조하여 표현하는 개념.

 

 !! 위에서 고양이에 대한 개념을

    속성(Property) : 고양이 이름, 묘종, 색깔, 나이의 데이터로 고양이의 정보를 표현

    메서드(Methods) : 우는 행동, 이동하는 것, 점프하는 것으로 고양이의 행동을 나타내는 기능을 표현함.

    internal class Cat
    {
        // 속성 정의
        public string catName;                                 // 이름은 뭔지
        public string catSpecies;                              // 묘종은 뭔지..
        public string color;                                   // 색은 뭔지..
        public int catAge;                                     // 나이는 몇 살인지..

        // 기능 정의
        public void meow()                                    // 야옹하고 울기
        {
            Console.WriteLine("Meow!");
        }

        public void moving()
        {
            Console.WriteLine(catName + " is moving...");      // 이동하기
        }


        public void jump()
        {
            Console.WriteLine("...Jump!");                      // 점프하기
        }
    }

 

  B. 캡슐화 (Encapuslation)

   : 데이터와 해당 데이터를 다루는 메서드를 하나의 단위로 묶어, 외부의 접근을 제어하고, 데이터를 보호하는 개념.

  

   !! 접근 제한자 (Access Modifier)
   : 객체 지향 프로그래밍에서 클래스의 멤버(필드, 메서드, 속성 등)에 대한 접근 권한을 지정하는 키워드
     
     1. public : 모든 외부의 접근을 허가함.
     2. private : 같은 클래스 내에서만 접근을 허가함.
     3. protected : 같은 클래스 내부와 파생 클래스에서 접근을 허가함.
     4. internal : 같은 **어셈블리 내에서 접근을 허가함.

      **어셈블리 (assembly)
       : NET 런타임 환경에서 실행할 수 있는 코드와 리소스, 메타데이터를 포장하고 배포하는 단위

 

     !! 위에서 고양이의 정보(데이터) 와 행동(메서드) 을 하나의 단위로 캡슐화(Cat 클래스) 하였고, 외부 코드(Main) 에서 Cat 클래스를 사용하여 고양이를 다룸.

 

  : 데이터의 보호를 위해서는 데이터의 선언을 필드로 직접 접근하기 보다는 속성(property) 으로 해야한다.

internal class Cat
{
    public string catName;     // !! 필드로 직접 접근하는 방식 : 외부 필드에서 변형이 가능하다.
    
    private string _catAge     // !! 속성으로 접근하는 방식 : 클래스 내에 _catAge 라는 속성을 추가.
    public string catAge       // !! 클래스 외부에서 private 필드에 접근하지 않고 속성을 통해 접근 가능해짐.
    {
        get { return _catAge; }
        set { _catAge = value; }
    }

 

  : 클래스의 속성을 통해, 외부에서 Cat 클래스의 인스턴스를 생성하거나 속성을 설정할 때, 원하는 방식으로 데이터 처리가 가능해진다.

internal class Cat
{
    public string catName;     // !! 필드로 직접 접근하는 방식 : 외부 필드에서 변형이 가능하다.
    
    private string _catAge     // !! 속성으로 접근하는 방식 : 클래스 내에 _catAge 라는 속성을 추가.
    public string catAge       // !! 클래스 외부에서 private 필드에 접근하지 않고 속성을 통해 접근 가능해짐.
    {
        get { return _catAge; }
        //set { _catAge = value; }
        set
        {
            if (value > 0)
            {
                _catAge = value;
            }
            else
            {
                console.WriteLine("유효하지 않는 나이입니다.");  // !! set 접근자의 조건문에 의해 나이가 잘못된 경우를 처리할 수 있게 됨.
                _catAge = 99;
            }
        }
    }
}

 

  C. 상속 (Inheritance)

  : 기존 클래스의 속성과 메서드를 새로운 클래스에서 재사용하거나 확장할 수 있게 해주는 개념.

 

  !! 고양이와 비슷한 맥락으로 강아지의 클래스를 만듦.

    internal class Dog
    {
        // 속성 정의
        private string name;
        public string Name                                  // 이름은 뭔지..
        {
            get { return name; }
            set { name = value; }
        }


        private string dogBreed;
        public string DogBreed                              // 견종은 뭔지..
        {
            get;                                            // !! 생략 가능함.
            set;
        }


        private string color;
        public string Color                                  // 색은 뭔지..
        {
            get;
            set;
        }

        
        private int age;
        public int Age                                       // 나이는 뭔지..
        {
            get { return age; }
            set
            {
                if (value > 0)
                {
                    age = value;
                }
                else
                {
                    Console.WriteLine("유효하지 않은 나이입니다.");
                    age = 99;
                }
            }
        }
        // 기능 정의
        public void bark()                                    // 왈!하고 울기
        {
            Console.WriteLine("Bark!");
        }

        public void moving()
        {
            Console.WriteLine(Name + " is moving...");        // 이동하기
        }


        public void jump()
        {
            Console.WriteLine("...Jump!");                    // 점프하기
        }
    }

>

 !! 고양이 클래스와 비교해보았을 때, 다음의 클래스 멤버는 공통적으로 사용이 가능함을 알 수 있다.

       속성 : 이름, 나이, 색
       메서드 : 이동, 점프

 !! 고양이와 강아지의 개념의 공통점은 동물이라는 점이다.

 

==> 해당하는 공유가능한 속성과 메서드를 상위 클래스(Animal) 로 추상화 시킨다면, 상위 클래스로부터 확장된 여러 개의 하위 클래스(Dog, Cat)에서 해당 속성과 메서드들을 간편하게 사용할 수 있다.

 

class Animal
{
    // properties
    private string name;
    public string Name
    {
        get;
        set;
    }

    private string color;
    public string Color
    {
        get;
        set;
    }
    
    private string age;
    public string Age
    {
        get;
        set;
    }
    
    // methods
    public void moving()
    {
        Console.WriteLine($"{Name}이 움직이기 시작합니다."};
    }
    
    
    public void jump()
    {
        Console.WriteLine("...JUMP!"};
    }
}

 

 

  !! 하위 클래스의 선언은 하위 클래스 : 상위 클래스 로 하여, 상속이 이루어지게 한다.

    internal class Dog : Animal
    {
        // properties
        private string dogBreed;
        public string DogBreed                              // 견종은 뭔지..
        {
            get;                                            // !! 생략 가능함.
            set;
        }

        // methods
        public void bark()                                    // 왈!하고 울기
        {
            Console.WriteLine("Bark!");
            Console.WriteLine($"{Name}은 발랄하게 가서 물었다");
        }
    }
    
    
    internal class Cat : Animal
    {
        // properties
        private string _catSpecies;
        public string catSpecies                              // 묘종은 뭔지..
        {
            get;
            set;
        }

        // methods
        public void meow()                                    // 야옹하고 울기
        {
            Console.WriteLine("Meow!");
            Console.WriteLine("아무 일도 일어나지 않았다.");
        }
    }

 

 !! Main에서 실행

Nyeaons & Mungs

    internal class Program
    {
        static void Main(string[] args)
        {
            Cat Nyeaons = new Cat();
            Nyeaons.Name = "Nyeaons";                     // 고양이의 이름은 냬옹스다.
            Nyeaons.catSpecies = "스코티시폴드";          // 묘종은 스코티시 폴드.
            Nyeaons.Color = "티끌없는 화이트";            // 색깔은 티끌없는 화이트.
            Nyeaons.Age = 5;                              // 나이는 5살.

            Console.WriteLine($"이 고양이의 이름은 {Nyeaons.Name}다. 묘종은 {Nyeaons.catSpecies}이며, {Nyeaons.Color}색을 지닌 귀여운 고양이지");
            Console.WriteLine($"나이는 {Nyeaons.Age.ToString()}살 이다\n\n");
            
            
            Console.WriteLine($"{Nyeaons.Name} 물어!");
            Nyeaons.moving();
            Nyeaons.jump();
            Nyeaons.meow();
            Console.WriteLine("\n\n");


            Dog Mungs = new Dog();
            Mungs.Name = "Mungs";                         // 강아지의 이름은 멍스다.
            Mungs.DogBreed = "웰시코기";                  // 견종은 웰시코기
            Mungs.Color = "갓 구운 빵과 같은 풍부한 브라운"; // 색깔은 이하 동문
            Mungs.Age = 3;                                // 나이는 3살

            Console.WriteLine($"이 강아지의 이름은 {Mungs.Name}다. 견종은 {Mungs.DogBreed}이며, {Mungs.Color}색을 지닌 귀여운 강아지라구");
            Console.WriteLine($"나이는 {Mungs.Age.ToString()}살 이다\n\n");

            Console.WriteLine($"{Mungs.Name} 물어!");
            Mungs.moving();
            Mungs.jump();
            Mungs.bark();
            Console.WriteLine("\n\n");
        }
    }

>

 

  D. 다형성 (Polymorphism)

: 어떤 객체의 속성이나 기능이 그 맥락에 따라 다른 역할을 수행할수 있는 객체 지향의 특성

 

메서드 오버라이딩 (Method Overriding)
: 상속 관계에서 자식 클래스가 부모 클래스의 메서드를 재정의하여 사용하는 것.
: 같은 이름의 기능이 다른 역할을 수행할 때 사용.

ex) 위의 코드에서 고양이와 강아지에게 물어! 라고 시켰는데 다른 결과가 나옴. 이를 동일한 이름으로 재정의 한 후, 메서드 오버라이딩을 수행 했을 때.
class Animal
{
    // properties
    private string name;
    public string Name
    {
        get;
        set;
    }
    
    // methods
    public virtual void Command()                                     // 해당 메서드는 오버라이드 가능하다 선언.
    {
        Console.WriteLine("동물에게 명령해보았다.");
        Console.WriteLine($"{Name}이 움직이기 시작합니다.");
        Console.WriteLine("...JUMP!");
    }
}

class Dog : Animal
{
    public override void Command()                                    // 왈!하고 울기
    { 
        Console.WriteLine($"{Name} 물어!");
        Console.WriteLine($"{Name}이 움직이기 시작합니다.");
        Console.WriteLine("...JUMP!");
        Console.WriteLine("Bark!");
        Console.WriteLine($"{Name}은 발랄하게 가서 물었다");
    }
}

class Cat : Animal
{
    public override void Command()                                   // 야옹하고 울기
    {
        Console.WriteLine($"{Name} 물어!");
        Console.WriteLine($"{Name}이 움직이기 시작합니다.");
        Console.WriteLine("...JUMP!");
        Console.WriteLine("Meow!");
        Console.WriteLine("아무 일도 일어나지 않았다.");
    }
}


==> Main에서 사용하는 명령 부분을 상당히 줄일 수 있었다.
        static void Main(string[] args)
        {
            Cat Nyeaons = new Cat();
            Nyeaons.Name = "Nyeaons";                     // 고양이의 이름은 냬옹스다.
            Nyeaons.catSpecies = "스코티시폴드";          // 묘종은 스코티시 폴드.
            Nyeaons.Color = "티끌없는 화이트";            // 색깔은 티끌없는 화이트.
            Nyeaons.Age = 5;                              // 나이는 5살.

            Console.WriteLine($"이 고양이의 이름은 {Nyeaons.Name}다. 묘종은 {Nyeaons.catSpecies}이며, {Nyeaons.Color}색을 지닌 귀여운 고양이지");
            Console.WriteLine($"나이는 {Nyeaons.Age.ToString()}살 이다\n\n");


            Nyeaons.Command();
            /* 이하의 부분이 생략됨.
            Console.WriteLine($"{Name} 물어!");
            Console.WriteLine($"{Name}이 움직이기 시작합니다.");
            Console.WriteLine("...JUMP!");
            Console.WriteLine("Meow!");
            Console.WriteLine("아무 일도 일어나지 않았다.");
            Console.WriteLine("\n\n");
            */
            
            // 생략...​


인터페이스 (Interface)
: 메서드의 이름, 매개변수, 반환 유형만을 선언없이 정의함으로써, 상속받는 클래스에 해당 메서드를 강제함.
: 다중 상속이 가능함.
: 클래스 간의 관계를 유연하게 설계할 수 있음.

ex) 각 동물의 소개를 Iintroduce라는 인터페이스를 활용하여 Main에 적용
    // 소개문 인터페이스
    internal interface Iintroduce
    {
        string Introduce();
    }
    
    internal class Dog : Animal, Iintroduce
    {
        //.. 생략
        
        public string Introduce()
        {
            return ($"이 강아지의 이름은 {Name}다. 견종은 {DogBreed}이며, {Color}색을 지닌 귀여운 강아지라구\n나이는 {Age.ToString()}살 이다\n\n");
        }
    }
    
    internal class Cat : Animal, Iintroduce
    {
        //.. 생략
        
        public string Introduce()
        {
            return ($"이 고양이의 이름은 {Name}다. 묘종은 {catSpecies}이며, {Color}색을 지닌 귀여운 고양이지\n나이는 {Age.ToString()}살 이다\n\n");
        }
    }​


==> 마찬가지로, main에서 일일히 적용시켜주지 않아도 됨.

    internal class Program
    {
        static void Main(string[] args)
        {
            Cat Nyeaons = new Cat();
            Nyeaons.Name = "Nyeaons";                     
            Nyeaons.catSpecies = "스코티시폴드";          
            Nyeaons.Color = "티끌없는 화이트";            
            Nyeaons.Age = 5;                              

            Console.WriteLine(Nyeaons.Introduce());       // 인터페이스로 인한 소개문 생략
            Nyeaons.Command();                            // 오버라이드로 인한 명령 부분 생략


            Dog Mungs = new Dog();
            Mungs.Name = "Mungs";                       
            Mungs.DogBreed = "웰시코기";                 
            Mungs.Color = "갓 구운 빵과 같은 풍부한 브라운";
            Mungs.Age = 3;                               

            Console.WriteLine(Mungs.Introduce());
            Mungs.Command();
        }
    }

>