파이썬과 객체지향 프로그래밍 - . 포함 (Composition) (심화)

11. 포함 (Composition) (심화)

Composition

  • 다른 클래스의 일부 기능을 그대로 이용하고 싶으나, 전체 기능 상속은 피하고 싶을 때 사용
  • Composition 또는 Aggregation 이라고도 한다.

  • 상속관계가 복잡할 경우, 코드 이해가 어려운 경우가 많음

  • 컴포지션은 명시적 선언, 상속은 암시적 선언
  • 다만, 컴포지션, 상속에 대한 개념 이해는 역시나 한번에 이해하고, 활용하기는 어려움
In [197]:
class Calc:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def add(self):
        return self.x + self.y

    def subtract(self):
        return self.x - self.y

class Calc2:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def add(self):
        return self.x + self.y

    def multiply(self):
        return self.x * self.y
  • Calc 클래스에서 Calc2의 multiply() 메서드를 활용하고 싶지만, Calc2 전체를 상속 받고 싶지는 않음
In [200]:
class Calc:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.calc2 = Calc2(x, y) # 해당 클래스의 객체를 명시적으로 가져옴
    
    def add(self):
        return self.x + self.y

    def subtract(self):
        return self.x - self.y
    
    def multiply(self):
        return self.calc2.multiply() # 해당 클래스의 객체에 있는 메서드를 명시적으로 활용함
한발짝 더 나가보기!(심화 문제)
다음 코드는 실제로 어떤 코드가 호출되었을지 이해해보자 (Composition 이해)
In [208]:
calc1 = Calc(1, 2)
print(calc1.multiply())
calc2 = Calc(x=2, y=3)  # 함수 인자와 마찬가지로 직접 인자명과 값을 써도 됨
print(calc2.multiply())
2
6
협업 과제(남에게 설명하면, 자신이 배운답니다.!)
클래스 설계해보기 1. 게임 캐릭터 클래스 만들기
- attribute: name, health_point, striking_power, defensive_power
- attribute 명시적 입력이 없으면, name='yourname', health_point=100, striking_power=3, defensive_power=3이 디폴트로 입력되도록 함
- method: info() - 게임 캐릭터 name, health_point, striking_power, defensive_power 출력
- 다음 시나리오가 동작할 수 있도록 작성
게임 캐릭터는 다음과 같이 3명이 존재함
Warrior
 - name: 원하는이름으로!
 - health_point = 100
 - striking_power = 3
 - defensive_power = 1
Elf
 - name: 원하는이름으로!
 - health_point = 100
 - striking_power = 1
 - defensive_power = 3
Wizard
 - name: 원하는이름으로!
 - health_point = 50 
 - striking_power = 2
 - defensive_power = 2
2. 각 3명의 캐릭터는 특별한 메서드를 가짐
게임 캐릭터는 다음과 같이 3명이 존재함
Warrior
 - attack: 상대방 객체를 입력받아서, '칼로 찌르다' 출력하고, 상대방의 receive 메서드를 호출해서, striking_power만큼 상대방의 health_point를 낮춰준다.
 - receive: 상대방의 striking_point를 입력으로 받아서, 자신의 health_point를 그만큼 낮추기, health_point가 0 이하이면 '죽었음' 출력
Elf
 - attack: 상대방 객체를 입력받아서, '마법을 쓰다' 출력하고, 상대방의 receive 메서드를 호출해서, striking_power만큼 상대방의 health_point를 낮춰준다.
 - receive: 상대방의 striking_point를 입력으로 받아서, 자신의 health_point를 그만큼 낮추기, health_point가 0 이하이면 '죽었음' 출력 
 - wear_manteau: 1번 공격을 막는다.
Wizard
 - attack: 상대방 객체를 입력받아서, '마법을 쓰다' 출력하고, 상대방의 receive 메서드를 호출해서, striking_power만큼 상대방의 health_point를 낮춰준다.
 - receive: 상대방의 striking_point를 입력으로 받아서, 자신의 health_point를 그만큼 낮추기, health_point가 0 이하이면 '죽었음' 출력 
 - use_wizard: 자신의 health_point를 3씩 올려준다.
3. 각 3명의 캐릭터 객체를 만들어서, attack 메서드를 호출해서 한 명의 캐릭터 죽여보기
과제작성이 다 끝나면, 손을 들어 강사와 코드를 검토합니다.
In [263]:
# 추상 클래스 선언하기
from abc import *

class Character(metaclass=ABCMeta):
    def __init__(self, name='yourname', health_point=100, striking_power=3, defensive_power=3):
        self.name = name
        self.health_point = health_point
        self.striking_power = striking_power
        self.defensive_power = defensive_power        
    
    def get_info(self):
        print (self.name, self.health_point, self.striking_power, self.defensive_power)
    
    @abstractmethod
    def attack(self, second):
        pass

    @abstractmethod
    def receive(self):
        pass

class Warrior(Character):
    def attack(self, second):
        print ('칼로 찌르다')
        second.receive(self.striking_power)
        
    def receive(self, striking_point):
        self.health_point = self.health_point - striking_point
        if self.health_point < 0:
            print ('죽었음')

class Elf(Character):
    def __init__(self, name='yourname', health_point=100, striking_power=3, defensive_power=3):
        super().__init__(name, health_point, striking_power, defensive_power)
        self.manteau = 0
        
    def attack(self, second):
        print ('칼로 찌르다')
        second.receive(self.striking_power)
        
    def receive(self, striking_point):
        if self.manteau < 1:
            self.health_point = self.health_point - striking_point
            if self.health_point < 0:
                print ('죽었음')
        else:
            self.manteau -= 0
    
    def wear_manteau(self):
        self.manteau += 1
In [264]:
warrior1 = Warrior()
warrior2 = Warrior()
elf1 = Elf(name='elf1', health_point=100, striking_power=1, defensive_power=3)

warrior1.attack(warrior2)
print (warrior2.health_point)
elf1.wear_manteau()
warrior1.attack(elf1)
print (elf1.health_point)
칼로 찌르다
97
칼로 찌르다
100
In [ ]:
 

협업 과제(남에게 설명하면, 자신이 배운답니다.!)
위의 코드도 작성이 되었다면, 다음 기능 추가해보기

  • method: change_name() - 게임 캐릭터 name 바꾸기
  • method: recover() - health_point를 최초 객체 생성시 입력된 값으로 변경
  • class method: howmany() - 현재 생성된 캐릭터 수를 출력 (이 기능은 심화부분까지 공부하신 분을 위함입니다.)
In [ ]: