본문 바로가기

Python

PyCharm - Oop (객체치향 프로그래밍)



IT 인프라

엔터프라이즈 IT 환경을 운영하고 관리하는 데 필요한 구성 요소
IT 서비스 및 솔루션을 제공하기 위해 필요한 IT 환경(서비스 및 플랫폼)을 구축, 개발, 운영하기 위해 필요한 모든 구성요소
플랫폼 - 하드웨어, 네트워킹요소, 데이터 스토리지
서비스 - 운영체제, 소프트웨어

IT 인프라 구성 방식
집약형 - 한대의 시스템으로 모든 기능을 해결
분산형 - 여러대의 시스템을 조합해서 하나의 시스템으로 구축

분산형 아키텍처
서버별로 다른 역할을 하도록 시스템을 수직으로 확장하는 구조 - c/s, 3-tier

수평 분할형 아키텍처
용도가 같은 서버를 늘려나가는 방식 - sharding, Partitioning

스탠바이형 아키텍처
물리 서버를 최소 두 대를 준비하여 한 대가 고장 나면 가동 중인 소프트웨어를 다른 한 대로 옮겨서 운영하는 방식
스탠바이, HA(High Availiability), Active-Standby 라고도 함


client-server : 화면 표시나 단순한 계산은 클라이언트에서 실행하고, 필요한 경우(데이터 요청 등) 서버에서 데이터 요청

3-tier 아키텍처
응용프로그램을 3개의 논리적 및 물리적 컴퓨팅 계층으로 구성하는 소프트웨어 아키텍처
프리젠테이션 계층 또는 사용자 인터페이스, 데이터를 처리하는 애플리케이션 계층,
그리고 애플리케이션과 연관된 데이터를 저장 및 관리하는 데이터 계층으로 구성
보다 신속한 개발, 확장성 개선, 안정성 향상, 보안성 강화

프레젠테이션 계층 (Presentation Tier)
일반 사용자가 애플리케이션과 상호작용하는 애플리케이션의 사용자 인터페이스 및 통신 계층
주요 목적은 정보를 표시하고 사용자로부터 정보를 수집하는 것

애플리케이션 계층
프리젠테이션 계층에서 수집된 정보를 다양한 비즈니스 규칙(로직)을 이용해서 처리함
프레젠테이션 계층에서 온 요청을 받아, 요구에 필요한 데이터를 데이터 계층에 요청하며
전달 받은 데이터를 특정 처리 및 가공을 하여 프레젠테이션 계층에 전달하는 것을 담당

데이터 계층
어플리케이션 계층의 요청에 따라 데이터 입출력 처리
데이터베이스와 데이터베이스에 접근하여 데이터를 읽거나 쓰는 것을 관리하는 계층


소프트웨어의 좋은 설계
유지보수가 용이해야 함
높은 응집도와 낮은 결합도를 가지도록 요소(모듈)를 적절히 배치하는 것 - 소프트웨어 설계 품질을 판단하는 기준
모듈: 크기와 상관없이 클래스나 패키지, 라이브러리와 같이 프로그램을 구성하는 임의의 요소를 의미

결합도Coupling: 다른 모듈과의 상호 의존성 정도, 모듈 간의 의존이 심하면 모듈의 독립성이 악화
모듈을 수정하려고 하는데, 다른 모듈이 영향을 받으면 수정하기 힘들 테니,
그 영향이 거의 없을수록 부담없이 수정이 가능

응집도Cohesion: 모듈에 포함된 내부 요소들이 하나의 책임/목적을 수행하기 위해 얼마나 잘 연관되었나의 정도
응집도가 높으면 수정해야 될 코드가 한 군데에 모여있기 때문에
하나(최소한)의 모듈만 분석해서 수정하면 되니 유지보수 하기 좋음

파이썬 객체지향 프로그래밍
OOP : object oriented programming
프로그램을 명령어들의 단순 묶음이라고 보는 시각에서
벗어나 독립된 객체들의 모음이라고 보는 시각에 근거해서
프로그래밍하는 패러다임

프로그램을 보다 유연하게 작성할 수 있고
프로그램 코드의 재사용을 높일수 있으며
대규모 소프트웨어 개발시 유지보수가 용이해짐

프로그램의 각 구성요소를 실제세계의 객체와 유사하게
디자인해서 클래스로 정의하는 것에 중점을 둠





# ex) 성적처리 프로그램
# 이름,국어,영어,수학을 입력하면
# 총점,평균,학점을 출력

# 성적 입력
name = input('이름은 ?')
kor = int(input('국어는 ?'))
eng = int(input('영어는 ?'))
mat = int(input('수학은 ?'))

# 성적 처리
tot = kor + eng + mat
avg = tot / 3
grd = '가'
if (avg >= 90): grd = '수'
elif (avg >= 80): grd = '우'
elif (avg >= 70): grd = '미'
elif (avg >= 60): grd = '양'

# 결과 출력
print('입력:%s %s %s %s' % (name, kor,eng,mat))
print('결과:%s %.1f %s' % (tot, avg, grd))









# ex) 성적처리 프로그램 II
# 함수 기반 프로그래밍 : 처리코드들을 하나의 이름으로 묶음
def readSungJuk():
    name = input('이름은 ?')
    kor = int(input('국어는 ?'))
    eng = int(input('영어는 ?'))
    mat = int(input('수학은 ?'))

    return name,kor,eng,mat

def computeSungJuk(kor,eng,mat):
    tot = kor + eng + mat
    avg = tot / 3
    grd = '가'
    if (avg >= 90):           grd = '수'
    elif (avg >= 80):         grd = '우'
    elif (avg >= 70):         grd = '미'
    elif (avg >= 60):         grd = '양'

    return tot,avg,grd

def printSungJuk(name,kor,eng,mat,tot,avg,grd):
    print('입력:%s %s %s %s' % (name,kor,eng,mat))
    print('결과:%s %.1f %s' % (tot,avg,grd))

# 프로그램 실행
name,kor,eng,mat = readSungJuk()
tot,avg,grd = computeSungJuk(kor,eng,mat)
printSungJuk(name,kor,eng,mat,tot,avg,grd)


# ex) 성적처리 프로그램 3
# 객체지향 프로그램 : 함수들과 관련된 변수들을 하나로 묶음
# 성적데이터를 담고있는 클래스와
# 성적처리에 필요한 기능들로만 구성된 클래스로 나눠 작성

# OOP에서의 클래스 특성
# 1. 값만 저장하는 클래스 : VO, DTO
# 2. 기능만 저장하는 클래스 : DAO, BO
# 데이터베이스 연동 클래스 : CRUD 기능 지원
# 3. UI처리만 저장하는 클래스 : UO

# OOP 3대 특성
# 캡슐화
# 상속
# 다형성

# OOP 5대 원칙
# 결합도, 응집도
# SOLID

# VO 클래스 만드는 법
# 생성자 : __init__
# private  멤버 변수 선언
# setter/getter : @setter, @property (@:데코레이터)

# 클래스 정의
# 클래스 이름은 camel 표기법으로 지음
# class 클래스명(상속여부):
#    생성자
#    setter/getter
#    메서드


class SungJukVO():   # 성적 데이터와 관련된 변수만 선언
      # 생성자 : 클래스 객체 생성시 초기화 작업 진행하는 함수
      # 생성자를 멤버변수 선언 및 기본값을 지정할 수 있음
    def __init__(self):
        # 멤버변수에 접근제한 기능 부여
        # 멤버명에 __를 추가하면 private 접근 제한 부여 메서드를통해서만 가능하게
        self.__name = '혜교'
        self.__kor = 0
        self.__eng = 0
        self.__mat = 0
        self.__tot = 0
        self.__avg = 0.0
        self.__grd = '가'

    # setter/getter
    # @property를 먼저 정의하고, @setter를 정의해야 함
    # @property
    # def 멤버 변수명(self)
    #     return self.멤버변수명

    # 멤버 변수명.@setter
    # def 멤버변수명(self, 멤버변수명):
    #     self.멤버변수명 = 멤버변수명

    @property           # 멤버 변수값을 외부로 반환
    def name(self):
        return self.__name

    @name.setter    # 외부에서 전달해준 값을 멤버변수에 대입
    def name(self, name):
        self.__name = name

    @property
    def kor(self):
        return self.__kor

    @kor.setter
    def kor(self, kor):
        self.__kor = kor

    @property
    def eng(self):
        return self.__eng

    @eng.setter
    def eng(self, eng):
        self.__eng = eng

    @property
    def mat(self):
        return self.__mat

    @mat.setter
    def mat(self, mat):
        self.__mat = mat

    @property
    def tot(self):
        return self.__tot

    @tot.setter
    def tot(self, tot):
        self.__tot = tot

    @property
    def avg(self):
        return self.__avg

    @avg.setter
    def avg(self, avg):
        self.__avg = avg

    @property
    def grd(self):
        return self.__grd

    @grd.setter
    def grd(self, grd):
        self.__grd = grd



# 클래스에 대한 변수(객체) 선언
# 변수명 = 클래스 생성자()
sj = SungJukVO()  #클래스만 만들면 따로따로 선언할 필요 없음 객체를 만드는 틀

# 객체의 멤버 변수에 접근 : 객체명.멤버변수명
# 멤버변수를 private으로 선언하면 은닉변수가 되어 접근불가 - 캡슐화
# 이럴 경우 setter/getter 으로 정의된 함수(메서드method)로만 접근가능


print(sj.name, sj.kor, sj.eng, sj.mat)  #바로 접근 불가에러


# 이름 : EmployeeVO
# 변수명 : empid, fname, lname, email,
#          phone, hdate, jobid, sal, comm,
#          mgrid, deptid









class EmployeeVO():
    def __init__(self):
        self.__empid = 000
        self.__fname = ''
        self.__lname = ''
        self.__email = ''
        self.__phone = ''
        self.__hdate = ''
        self.__jobid = ''
        self.__sal = 0
        self.__comm = 0.0
        self.__mgrid = 0
        self.__deptid = 0


    @property
    def empid(self):
        return self.__empid

    @empid.setter
    def empid(self, empid):
        self.__empid = empid

    @property
    def fname(self):
        return self.__fname

    @fname.setter
    def fname(self, fname):
        self.__fname = fname

    @property
    def lname(self):
        return self.__lname

    @lname.setter
    def lname(self, lname):
        self.__lname = lname

    @property
    def email(self):
        return self.__email

    @email.setter
    def email(self, email):
        self.__email = email

    @property
    def phone(self):
        return self.__phone

    @phone.setter
    def phone(self, phone):
        self.__phone = phone

    @property
    def hdate(self):
        return self.__hdate

    @hdate.setter
    def hdate(self, hdate):
        self.__hdate = hdate

    @property
    def jobid(self):
        return self.__jobid

    @jobid.setter
    def jobid(self, jobid):
        self.__jobid = jobid

    @property
    def sal(self):
        return self.__sal

    @sal.setter
    def sal(self, sal):
        self.__sal = sal

    @property
    def comm(self):
        return self.__comm

    @comm.setter
    def comm(self, comm):
        self.__comm = comm

    @property
    def mgrid(self):
        return self.__mgrid

    @mgrid.setter
    def mgrid(self, mgrid):
        self.__mgrid = mgrid

    @property
    def deptid(self):
        return self.__deptid

    @deptid.setter
    def deptid(self, deptid):
        self.__deptid = deptid



emp = EmployeeVO()

print(emp.empid, emp.fname, emp.lname, emp.email,
         emp.phone, emp.hdate, emp.jobid, emp.sal, emp.comm,
          emp.mgrid, emp.deptid)




steve = EmployeeVO()
print(steve.fname, steve.lname)
steve.fname = 'steve'
# setter에 의해 값이 멤버변수에 대입
steve.lname= 'king'

print(steve.fname, steve.lname)

 

 

# ex) 성적처리 프로그램 3
# 객체지향 프로그램 : 함수들과 관련된 변수들을 하나로 묶음
# 성적데이터를 담고있는 클래스와
# 성적처리에 필요한 기능들로만 구성된 클래스로 나눠 작성

# OOP에서의 클래스 특성
# 1. 값만 저장하는 클래스 : VO, DTO
# 2. 기능만 저장하는 클래스 : DAO, BO
class SungJukVO():
    # 객체 생성시 초기화할 멤버변수에 대한 매개변수 선언
    def __init__(self, name, kor, eng, mat):
        self.name = name
        self.kor = kor
        self.eng = eng
        self.mat = mat
        self.__tot = 0
        self.__avg = 0.0
        self.__grd = '가'

    # 응집도 관점에서 볼때 성적처리 코드가
    # getter 별로 흩어져 있음
    # -> 독립적인 메서드로 작성할것 추천!

    @property
    def __tot(self):
        self.__tot = self.kor + self.eng + self.mat
        return self.__tot

    @property
    def __avg(self):
        self.__avg = self.__tot / 3
        return self.__avg

    @property
    def __grd(self):
        if self.__avg >= 90 : self.__grd ='수'
        elif self.__avg >= 80 : self.__grd ='우'
        elif self.__avg >= 70 : self.__grd ='미'
        elif self.__avg >= 60 : self.__grd ='양'
        return self.__grd


# 성적 객체 생성
suji = SungJukVO('수지', 98, 78, 67)

print(suji.name, suji.kor, suji.eng, suji.mat)

suji.name = '지수'  # 특정 속성의 값을 변경 (신뢰성에 영향안줌)

# 수지성적처리 : 클래스 외부에서 수행
suji.tot = suji.kor + suji.eng + suji.mat
print(suji.tot)

# 정상적이지 않은 방식으로 총점을 대입    -> 신뢰성 저하
suji.tot = 299  # 비추! -> tot를 private하게 선언   tot는 세개의 점수의 합이기때문에

# 클래스 내부에 선언된 getter를 이용해 성적결과 알아보기
print(suji.tot, suji.avg, suji.grd)




# 개선된 성적 클래스
class SungJukVO():
    # self : 클래스의 멤버변수임을 나타내기 위한 지시자
    # 매개변수 shadowing을 방지하기 위해 사용
    # 또한 메서드의 첫 매개변수는 self로 선언해야함
    def __init__(self, name, kor, eng, mat):
        self.name = name
        self.kor = kor
        self.eng = eng
        self.mat = mat
        self.__tot = 0
        self.__avg = 0.0
        self.__grd = '가'



    # __str__ 멤버변수들의 값을 문자열화 해서
    # 객체의 정보를 외부에 표현할 때 사용
    def __str__(self):
        self.computeSungjuk()
        result = f'{self.name}\n{self.kor},' \
                 f'{self.eng},{self.mat}\n' \
                 f'{self.__tot},{self.__avg},{self.__grd}'
        return result



    # 성적처리
    # 메서드도 private 프라이빗 선언하려면 : __메서드명
    def computeSungjuk(self):
        self.__tot = self.kor + self.eng + self.mat
        self.__avg = self.__tot / 3
        if self.__avg >= 90 : self.__grd ='수'
        elif self.__avg >= 80 : self.__grd ='우'
        elif self.__avg >= 70 : self.__grd ='미'
        elif self.__avg >= 60 : self.__grd ='양'


jihyun = SungJukVO('지현', 98, 78, 67)
# 클래스에 정의된 __str__를 호출해서 객체 정보 출력
print(jihyun)











# 성적데이터를 담고있는 클래스(SungJukVO)와
# 성적처리에 필요한 기능들로만 구성된 클래스(SungJukService)로 나눠 작성
class SungJukVO():
    def __init__(self, name, kor, eng, mat):
        self.name = name
        self.kor = kor
        self.eng = eng
        self.mat = mat
        self.__tot = 0
        self.__avg = 0.0
        self.__grd = '가'

    def __str__(self):
        result = f'{self.name},{self.kor},' \
                 f'{self.eng},{self.mat},' \
                 f'{self.__tot},{self.__avg},{self.__grd}'
        return result



    # private 으로 선언된 tot, avg, grd에 대한 setter 정의
    @property
    def tot(self):
        return self.__tot

    @tot.setter
    def tot(self, tot):
        self.__tot = tot

    @property
    def avg(self):
        return self.__avg

    @avg.setter
    def avg(self, avg):
        self.__avg = avg

    @property
    def grd(self):
        return self.__grd

    @grd.setter
    def grd(self, grd):
        self.__grd = grd



class SungJukService():
    # 성적데이터 입력  SungJukVO 객체생성
    def readSungJuk(self):
        name = input('이름은 ?')
        kor = int(input('국어는 ?'))
        eng = int(input('영어는 ?'))
        mat = int(input('수학은 ?'))

        return SungJukVO(name, kor, eng, mat)



    # SungJukVO 형식의 변수 sj를 이용해서
    # 총점, 평균, 학점 계산
    def computeSungjuk(self, sj):
        sj.tot = sj.kor + sj.eng + sj.mat
        sj.avg = sj.tot / 3
        if sj.avg >= 90 : sj.grd ='수'
        elif sj.avg >= 80 : sj.grd ='우'
        elif sj.avg >= 70 : sj.grd ='미'
        elif sj.avg >= 60 : sj.grd ='양'


# compute 출력



# 테스트
sjsrv = SungJukService()

sjvo = sjsrv.readSungJuk()
sjsrv.computeSungjuk(sjvo)

print(sjvo)








# vo : value object


# 회원가입을 처리하는 클래스 생성
# MemberVO :
#        userid, passwd, name, email, milege, grade
# MemberService :
#   registerMember : 회원정보 입력
#   processMemberGrade : 마일리지에 따라 회원등급 결정
#      vip : 마일리지 - 10000이상
#      gold : 마일리지 - 7000이상
#      silver : 마일리지 - 4000이상
#      bronze : 마일리지 - 2000이상
#      member : 그외


class MemberVO():
    def __init__(self, userid, passwd, name, email, milege):
        self.userid = userid
        self.passwd = passwd
        self.name = name
        self.email = email
        self.milege = milege
        self.grade = 'member'

    def __str__(self):
        result = f'{self.userid}{self.passwd}{self.name}{self.email}{self.milege}{self.grade}'
        return result



class MemberService():
    # 성적데이터 입력  SungJukVO 객체생성
    def registerMember(self):
        userid = input('아이디은 ?')
        passwd = input('비번은 ?')
        name = input('이름은 ?')
        email = input('이메일은 ?')
        milege = int(input('마일리지는 ?'))


        return MemberVO(userid, passwd, name, email, milege)


    def processMembergrade(self, mbvo):
        if mbvo.milege >= 10000 : mbvo.grade = 'vip'
        elif mbvo.milege >= 7000 : mbvo.grade = 'gold'
        elif mbvo.milege >= 4000 : mbvo.grade = 'silver'
        elif mbvo.milege >= 1000 : mbvo.grade = 'bronze'


mbsrv = MemberService()
mbvo = mbsrv.registerMember()
mbsrv.processMembergrade(mbvo)
print(mbvo)



# setter 접근 제한하기











import inspect
# inspect 모듈 이용
# 특정 setter의 호출이 정상적인지 확인

class SungJukVO():
    def __init__(self, name, kor, eng, mat):
        self.name = name
        self.kor = kor
        self.eng = eng
        self.mat = mat
        self.__tot = 0
        self.__avg = 0.0
        self.__grd = '가'

    def __str__(self):
        result = f'{self.name} {self.kor} ' \
                 f'{self.eng} {self.mat} ' \
                 f'{self.__tot} {self.__avg} {self.__grd}'
        return result

    # private으로 선언된 tot,avg,grd에 대한 setter 정의
    @property
    def tot(self): return self.__tot

    @tot.setter
    def tot(self, tot):
        hasattr = self.__check_caller(inspect.stack()[1])
        if hasattr: self.__tot = tot
        else: raise AttributeError("Can't set value!!!")

    @property
    def avg(self): return self.__avg

    @avg.setter
    def avg(self, avg):
        hasattr = self.__check_caller(inspect.stack()[1])
        if hasattr: self.__avg = avg
        else: raise AttributeError("Can't set value!!!")

    @property
    def grd(self): return self.__grd

    @grd.setter
    def grd(self, grd):
        hasattr = self.__check_caller(inspect.stack()[1])
        if hasattr: self.__grd = grd
        else: raise AttributeError("Can't set value!!!")

    def __check_caller(self, caller):
        callattr = False
        fnattr = caller.function
        if hasattr(SungJukService, fnattr):
            callattr = True

        return callattr