본문 바로가기
개발

[파이썬&루비] 캡슐화(Encapsulation)

by 마스터누누 2017. 6. 1.
728x90
반응형

캡슐화(Encapsulation)



객체는 규모 있는 애플리케이션을 만드는 과정에서 필요한 기능이다.

따라서 일반적인 프로그래밍 과정에서 객체의 필요성에 대해 못 느낄 수도 있다.

객체는 부품과 같다. 이러한 객체를 조립해서 큰 프로젝트를 만들어 가는 것이다.

따라서 좋은 객체를 만드는 것은 좋은 부품을 만드는 것이며, 이를 위해서 여러가지 조건을 충족해야한다.

그 중에 하나가 인캡슐레이션, 즉 캡슐화이다.


캡슐화란 객체가 어떤 일을 하는지와 상관없이 견고한 케이스로 감싸듯 외부의 영향을 받지 않게 하는 것이다.

즉, 이러한 케이스, 캡슐을 씌우는 것은 외부로부터 영향을 받아 변경되지 않도록 하는 것이다.

함수나 변수도 캡슐로 감싼 예 중의 하나이다.

함수의 조건문 반복문 등도 함수라는 캡슐로 싼 것이나 다름없고, 이러한 함수를 다시 묶어서 모듈로 감싼것이다.

객체도 이와 같은 맥락에서 내부의 동작을 보호하기 위한 캡슐이라고 할 수있다.





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
# python
class C(object):
    def __init__(self, v):
        self.value = v
    def show(self):
        print(self.value)
 
c1 = C(10)
print(c1.value)
c1.value = 20
print(c1.value)
c1.show()
 
# ruby
class C
  def initialize(v)
    @value = v
  end
  def show()
    p @value
  end
end
c1 = C.new(10)
# p c1.value
# c1.value = 20
c1.show()
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
36
# python
class C(object):
    def __init__(self, v):
        self.value = v
    def show(self):
        print(self.value)
    def getValue(self):
        return self.value
    def setValue(self, v):
        self.value = v
c1 = C(10)
print(c1.getValue())
c1.setValue(20)
print(c1.getValue())
 
# ruby
class C
  def initialize(v)
    @value = v
  end
  def show()
    p @value
  end
  def getValue()
    return @value
  end
  def setValue(v)
    @value = v
  end
end
c1 = C.new(10)
# p c1.value
p c1.getValue()
# c1.value = 20
c1.setValue(20)
p c1.getValue()
cs


다른 객체지향 언어, 예를 들어 자바에서 사용하듯이 setter/getter 메소드를 사용해야한다.

setter는 변수를 setting하는 함수이며, getter는 변수를 가져오는 함수이다.

이는 임의로 변수로 접근하여 수정을 할 경우 자체적으로 assertion이 실행되는 특징이 있다.

이 때문에 좀 더 오류의 가능성이 적어지며 효율적인 코드 작성이 가능해진다.


setter나 getter 함수는 어떠한 이름을 사용해도 상관이 없으나

일반적으로 set변수이름/get변수이름 으로 사용하게 된다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# python
class Cal(object):
    def __init__(self, v1, v2):
        if isinstance(v1, int):
            self.v1 = v1
        if isinstance(v2, int):
            self.v2 = v2
    def add(self):
        return self.v1+self.v2
    def subtract(self):
        return self.v1-self.v2
    def setV1(self, v):
        if isinstance(v, int):
            self.v1 = v
    def getV1(self):
        return self.v1
c1 = Cal(10,10)
print(c1.add())
print(c1.subtract())
c1.setV1('one')
c1.v2 = 30
print(c1.add())
print(c1.subtract())
 
cs


위의 코드는 파이썬에 대한 예제이다.

파이썬에서는 isinstance(변수, 데이터형)으로 변수의 설정을 규제할 수 있다.

즉, 생성자의 데이터 값을 검증하는 것이다.

이를 assertion 과정이라고 하며 다른 데이터형으로 바뀌어 오류가 생기는것을 방지할 수 있다.

set 메소드도 마찬가지로 이렇게 데이터 형을 판단한뒤 인스턴스 변수를 변경하게 된다.


파이썬은 setter/getter 함수와 함께 데이터에 직접 접근하는 것을 막지 못한다.

따라서 이와같은 활용 방법은 권장되는 사항이지만 반드시 사용하는게 좋다.

기능이 좋은 IDE의 경우에는 객체 생성시 set/get이 자동으로 생성된다.





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
#ruby
class Cal
  attr_reader :v1, :v2
  attr_writer :v1
  def initialize(v1,v2)
    @v1 = v1
    @v2 = v2
  end
  def add()
    return @v1+@v2
  end
  def subtract()
    return @v1-@v2
  end
  def setV1(v)
    if v.is_a?(Integer)
      @v1 = v
    end
  end
  def getV1()
    return @v1
  end
end
c1 = Cal.new(10,10)
p c1.add()
p c1.subtract()
c1.setV1('one')
c1.v1 = 20
p c1.add()
c1.getV1()
cs


다음은 루비로 넘어가서 예제코드를 살펴보자

변수.is_a?(데이터형) 의 형식으로 assertion이 가능하다.

만약 setter 함수에 옳지 못한 데이터형이 들어 올 경우 인스턴스 변수 변경이 무시된다.


루비는 setter/getter를 사용하지 않는 경우는 변수에 대한 호출이나 변경이 불가능하다.

예를 들어 getter만 제공한 경우에는 인스턴스 변수에 대한 변경이 불가능하게된다.

이는 루비가 파이썬보다 변수에 대한 정책이 엄격하기 때문이다.




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
# python
class C(object):
    def __init__(self, v):
        self.__value = v
    def show(self):
        print(self.__value)
c1 = C(10)
#print(c1.__value)
c1.show()
 
# ruby
class C
  #attr_reader :value
  #attr_writer :value
  attr_accessor :value
  def initialize(v)
    @value = v
  end
  def show()
    p @value
  end
end
c1 = C.new(10)
p c1.value
c1.value = 20
p c1.value
cs


그렇다면 파이썬에서는 어떻게 인스턴스 변수에 접근을 못하게 막을수 있을까?

반대로 루비에서는 어떻게 인스턴스 변수에 접근을 할 수 있을까?


파이썬에서는 변수의 이름앞에 __를 붙여주면 된다.

__는 _(언더바)가 2개 붙어있는 형태이다.

이렇게 언더바가 2개 붙어지면 외부에서 접근할수 없는 변수가 된다. 


위의 루비 코드에서 attr_reader는 변수를 읽기 가능으로 설정하는 키워드이다.

또한, attr_write는 변수를 쓰기 가능으로 설정하는 키워드이다.

마지막으로  수정/삭제가 가능해진다.


이렇게 외부에서 접근이 가능한 변수를 파이썬에서는 property, 루비에서는 attribute라고 한다.



출처 : 생활코딩, 파이썬&루비

반응형

댓글