티스토리 뷰



이번 포스팅은 지난 VB.Net 객체지향프로그래밍의 실제1에 이어서 VB.Net의 객체 지향 프로그래밍과 관련한 나머지 사항을 나눌까 합니다.



■ 접근 변경자(Access Modifiers)

특정 클래스 앞이나 클래스내 속성이나 함수에 붙어서 그 성격을 한정하는 변경자(Modifier)는 객체 지향 프로그래밍 과정에서 가장 빈번하게 만나는 키워드가 아닐까 싶습니다. 

Namespace Customer

    Public Class CustBase
        Public name As String

        Public Sub New(ByVal newname As String)
            name = newname
        End Sub

        Sub CallTest()
            MyClass.Calling()
            Me.Calling()
        End Sub

        Overridable Sub Calling()
            MessageBox.Show(name & " 고객님이 호출하셨습니다!")
        End Sub
    End Class

    Public Class PriCust
        Inherits CustBase

        Private weight As Double

        Public Sub New(ByVal newname As String, ByVal newweight As Double)
            MyBase.New(newname)
            weight = newweight
        End Sub
    End Class

    Public Class CoCust
        Inherits CustBase

        Dim ceoname As String

        Public Sub New(ByVal newname As String, ByVal newceo As String)
            MyBase.New(newname)
            ceoname = newceo
        End Sub

        Public Overrides Sub Calling()
            MsgBox(name & " 법인에서 호출하셨습니다!")
        End Sub
    End Class

End Namespace

접근 범위는 컨텍스트(Context) 블럭 범위 즉 현재 항목을 감싸고 있는 블럭의 내부 또는 외부에서의 접근 가능 여부를 지정하는 것과 상속한 클래스에서의 접근 가능 여부를 다루게 됩니다. 위의 코드 예제에서 CustBase, PriCust,CoCust 클래스는 Customer 네임스페이스 블럭으로 묶여있고 각 클래스 내부의 속성과 함수들은 클래스 블럭으로 묶여 있어서 접근 변경자를 어떻게 지정하는 가에 따라 블럭 외부에서의 접근 가능 여부와 상속한 자식 클래스에서의 접근 가능 여부에 변화가 생깁니다. Public, Private등 익숙한 변경자와 함께 다른 변경자는 무엇이 있는지 각각의 의미는 무엇인지 아래에서 하나씩 나누어 보겠습니다.

  • Private : 블럭 내부에서만 접근할 수 있습니다. 클래스, 모듈, 구조체 수준에서 지정할 수 있습니다. 접근 변경자가 없으면 기본적으로 Private으로 간주합니다. 클래스 속성이나 메소드를 Private으로 선언한 경우에는 상속받은 클래스에서도 접근할 수 없습니다. 예제 코드에서 CallTest(), weight, ceoname등을 들수 있습니다.

  • Public : 어떠한 접근 제한도 두지 않는 방법입니다. 클래스, 네임스페이스, 모듈, 구조체, 인터페이스, 소스 파일 수준에서 지정할 수 있습니다.

  • Protected : 자신의 클래스나 상속받은 자식클래스에서만 접근할 있습니다. Private에서 상속 받은 자식클래스로 접근 범위를 확장한 개념이며 클래스 블럭에서만 지정할 수 있습니다.

  • Friend : Public에서 접근 범위를 현재 응용프로그램(어셈블리, Assembly)으로 축소시킨 개념으로 클래스, 네임스페이스, 모듈, 구조체, 인터페이스, 소스 파일 수준에서 지정할 수 있습니다.

  • Protected Friend : Protected와 Friend 성격을 모두 갖는 변경자로 클래스 블럭에서만 지정할 수 있습니다.


■ 클래스 속성과 클래스 메소드

통상 클래스의 속성과 메소드는 오브젝트를 생성해야만 사용할 수 있고 각 오브젝트는 별도의 공간에 생성되어 각각 독립적으로 관리됩니다. 반면에 OOP 개념에서는 클래스 속성과 클래스 메소드라는 개념을 사용하는데 이는 클래스의 모든 오브젝트가 동일한 속성과 메소드를 공유하는 개념으로 C클래스의 A라는 오브젝트에서 Attr이라는 클래스 속성에 10을 저장했다면 C클래스의 B라는 오브젝트에서 Attr이라는 클래스 속성에 접근하면 A오브젝트에서 저장했던 10 값 그대로를 참조하게되는 것입니다.

클래스 속성과 메소드를 정의하려면 위의 접근 변경자 처럼 앞에 "Shared" 키워드를 붙여주면 됩니다. 클래스 속성과 메소드는 오브젝트를 선언하지 않고도 "클래스명.속성"의 형식으로 직접 참조할 수 있습니다. 주의할 점은 메소드를 클래스 메소드로 만들어서 오브젝트를 선언하지 않고도 사용할 수는 있지만 클래스 메소드에서는 클래스 속성과 클래스 메소드에만 접근할 수 있고 다른 일반 멤버는 접근할 수 없다는 것입니다.

클래스 나름의 상수값 정의나 공통 함수등에 사용할 수 있습니다.


■ 다형성(Polymorphism)

OOP의 대표적인 특성인 다형성은 VB.Net에서 상속(Inheritance)을 기반으로 구현하는 방법과 인터페이스(Interface)를 기반으로 구현하는 방법이 있습니다. 인터페이스에 대해서는 계속 나누기로 하고 우선 오버로딩(Overloading), 오버라이딩(Overriding), 쉐도잉(Shadowing)을 다루겠습니다.

오버로딩(Overloading)은 동일한 메소드 이름이지만 메소드의 파라미터 타입이나 파라미터 개수가 다른 여러개의 메소드를 정의해 놓으면 메소드 호출 시점에 파라미터 타입과 개수에 따라 적절한 메소드가 자동 연결되도록 하는 것입니다. 오버로딩하면 한번씩 언급되는 기술이 있는데 바로 연산자 오버로딩(Operator overloading) 입니다. "10 + 20"과 같은 단순한 문장 조차도 두개의 인수를 받아 더하기를 처리하는 여러개의 더하기 함수가 존재한다고 할 수 있습니다. 숫자 리터럴의 경우에도 정수, 실수등 여러 타입이 존재하고 각 타입에 따라 처리하는 방식이 다르기 때문입니다. 이처럼 사용자 클래스에 VB.Net의 연산자를 적용하기 위하여 연산자별 함수를 만들어 가는 것을 연산자 오버로딩이라 합니다.

        Public Shared Operator +(ByVal a As CustBase, ByVal b As CustBase) As CustBase
            Return New CustBase(a.name & " " & b.name)
        End Operator
        '......
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim cust3 As Customer.CustBase = cs1 + cs2
        cust3.Calling()
    End Sub

위의 코드는 "Operator +"라는 구문을 통해서 더하기 연산자를 오버로딩하고 있으며 CustBase 오브젝트 간의 더하기를 수행하면 자동으로 이 함수가 호출됩니다. 더하기 연산자처럼 VB.Net에서 오버로딩 할 수 있는 연산자는 +, -, *, \, /, ^, &, Like, Mod, And, Or, Xor, Not, << , >> , =, <> , < , <=, > , >=, CType, IsTrue, IsFalse 등이 있습니다.

오버라이딩(Overriding)은 예제 코드의 Calling() 메소드에서 적용한 기술로 부모클래스와 동일한 이름의 메소드를 재정의해서 부모클래스와는 다른 동작을 시킬때 사용합니다. 메소드 재정의와 연관된 키워드는 아래와 같습니다.

  • Overridable : 자식 클래스에서 재정의 할 수 있음을 나타냅니다.
  • NotOverridable : 재정의를 금지시킵니다. Public 메소드는 기본적으로 재정의 금지 상태입니다.
  • MustOverride 자식 클래스에서 반드시 재정의하도록 강제 합니다. Mustlnherit 클래스에서 사용합니다.
  • Overrides : 자식 클래스에서 재정의 가능한 메소드를 재정의 합니다.

쉐도잉(Shadowing)은 자식 클래스에서 부모 클래스와 이름이 동일한 속성이나 메소드를 재정의가 아닌 방식으로 사용할 수 있도록 합니다. 부모 클래스에 있는 속성이나 메소드를 숨긴다는 의미입니다.

Public Shadows name As String

자식 클래스인 CoCust에 부모 클래스의 속성과 동일한 이름의 name을 선언하면 컴파일러는 오류를 내지만 "Shadows"를 붙이면 동일한 이름에도 불구하고 자식 클래스의 오브젝트에서 사용할 수 있도록 지원합니다.


■ 인터페이스(Interface)

OOP 다형성 구현의 또다른 방식인 인터페이스는 속성이나 메소드의 실제 코드 없이 정의만을 모아둔 것으로 각 사용자 클래스에서는 인터페이스에 정의한 속성이나 메소드의 실제 코드를 작성하여(Implement) 서로 다른 클래스의 오브젝트에 대해서 동일한 이름의 속성이나 메소드를 사용 할 수있도록 해줍니다.

    Public Interface CustInterface
        Sub SetName(ByVal Name As String)
        Function GetName() As String
    End Interface

    Public Class CoCust
        Inherits CustBase
        Implements CustInterface

        Sub SetName(ByVal Name As String) Implements CustInterface.SetName
            ceoname = Name
        End Sub
        Function GetName() As String Implements CustInterface.GetName
            Return ceoname
        End Function

        Dim ceoname As String

        Public Sub New(ByVal newname As String, ByVal newceo As String)
            MyBase.New(newname)
            ceoname = newceo
        End Sub

        Public Overrides Sub Calling()
            MsgBox(name & " 법인에서 호출하셨습니다!")
        End Sub
    End Class

인터페이스 정의는 "Interface"를 사용하며 위의 코드 예제처럼 인터페이스 내부에는 메소드 또는 속성에 대한 프로토타입 정의만 존재할 수 있습니다. 사용자 클래스에서 인터페이스를 구현할 때는 클래스 상속처럼 "Implements 인터페이스명"의 형식으로 기술할 수 있고 실제 구현하는 메소드 뒤에도 "Implements 인터페이스명.항목명"의 형식으로 기술하면 됩니다. 클래스 상속과의 차이점은 클래스 상속의 경우에는 베이스 클래스(Base Class) 하나로부터만 상속이 가능하지만 인터페이스 구현의 경우에는 Implements 구문에서 여러 인터페이스를 콤마(,)로 구분하여 기술할 수 있습니다. 닷넷 클래스 라이브러리에서도 "ILIst" 처럼 "I"로 시작하는 다양한 시스템 인터페이스를 제공하고 있습니다. 인터페이스를 파라미터로 사용하면 해당 인터페이스를 구현하고 있는 다양한 클래스를 하나의 인수로 받아서 처리할 수 있는 무한 확장성을 갖게 됩니다.


■ 속성(Property)과 변수(Variable)

통상 클래스 내에서 정보를 저장하는 변수를 속성으로 지칭하기도 하지만 VB.Net에서는 단순 변수 선언과 속성을 명확하게 구분짓고 있습니다. 선언한 단순 변수를 필드(Field)라 지칭합니다. 속성은 "Public Property Name As String" 처럼 Property라는 키워드를 변수 선언에 사용하거나 아래의 예제 코드 처럼 Property ...  End Property 문장으로 속성 프로시저(Property Procedure)를 작성하는 방식으로 선언할 수 있습니다.

    Public Class PriCust
        Inherits CustBase

        Dim _firstName, _lastName As String
        Property fullName() As String
            Get
                If _lastName = "" Then
                    Return _firstName
                Else
                    Return _firstName & " " & _lastName
                End If

            End Get
            Set(ByVal Value As String)
                Dim _names As String() = Value.Split(New Char() {" "c})
                If _names.Length <> 2 Then
                    _firstName = Value
                    _lastName = ""
                Else
                    _firstName = _names(0)
                    _lastName = _names(1)
                End If
            End Set
        End Property

        Private weight As Double

        Public Sub New(ByVal newname As String, ByVal newweight As Double)
            MyBase.New(newname)
            weight = newweight
        End Sub
    End Class

변수 선언에 Property를 붙이는 단순한 속성 정의 방법을 "자동 구현 속성"(Auto-Implemented Properties)이라 하며 내부에 임의의 필드를 정의하고 단순한 Get, Set을 구현한 것과 동일한 효과를 가져옵니다. 속성 정의 내부에 Get, Set을 정의 하지만 일반 변수에 대하여 fullName = "Hong gildong"로 사용하면 Set이 수행되고 MsgBox(fullName) 처럼 참조하면 Get이 수행되는 방식처럼 읽기/쓰기가 루틴을 통해서 이루어지는 것이 일반 변수와의 큰 차이점입니다. 이런 특성을 통해서 개발자는 속성 읽기/쓰기에 나름의 기능을 반영할 수 있고 단순한 저장소가 아닌 다양한 자원을 속성으로 가질 수 있습니다.


댓글
댓글쓰기 폼