프로그래밍을 시작하면서 처음부터 접하게 되는 게 Objective Orientied Programming (OOP, 객체 지향 프로그래밍)이다.
OOP는 소프트웨어 개발에서 일반적으로 사용되는 패러다임 중 하나이다. OOP는 코드를 더 모듈화 하고 재사용 가능한 댄위로 구성하며, 코드를 더 쉽게 이해하고 유지보수할 수 있게 해 준다.
그렇다면 정확하게 정의를 한다면..
객체 지향 프로그래밍이란 - What is Objective Oriented Programming
객체 지향 프로그래밍의 주요 특징 중 하나는 캡슐화이다.
캡슐화는 데이터와 데이터를 조작하는 메서드를 하나의 단위로 묶어서 외부에 노출시키지 않는 것을 하는데,
이는 데이터를 보호하고 유효성을 검사하는 등의 역할을 수행하며,
코드의 안정성을 높이는 데 중요한 역할을 합니다.
또한 객체 지향 프로그래밍에서는 상속과 다형성도 중요한 개념이 된다.
상속은 하위 클래스가 상위 클래스의 속성과 메서드를 상속받을 수 있는 기능이며,
이를 통해 코드의 재사용성을 높이고, 코드를 더 간결하게 작성할 수 있다.
다형성은 같은 이름의 메서드가 다른 객체에서 다른 동작을 수행할 수 있는 기능이고.
이를 통해 코드의 유연성을 높일 수 있으며,
객체 지향 프로그래밍의 가장 큰 장점 중 하나이다.
객체 지향 프로그래밍은 이러한 특징들을 결합하여
코드의 모듈화,
재사용성,
유지보수성,
확장성을 높일 수 있는 프로그래밍 패러다임이다.
따라서 객체 지향 프로그래밍은 현대 소프트웨어 개발에서 매우 중요한 역할을 한다.
여기서 SOLID Principle이라는 원칙이 나오게 된다.. 왜일까?
SOLID 원칙은 객체 지향 프로그래밍(OOP)에서
소프트웨어 시스템을 유지보수 가능하고 유연하며 확장 가능하게 만들기 위해 적용할 수 있는 원칙이다.
SOLID 원칙은 각각 소프트웨어를 시간이 지나도 쉽게 수정하고 확장할 수 있도록
개발자들이 코드를 작성하는 데 도움이 되도록 설계하는 원칙이다.
전반적으로 SOLID 원칙은 OOP 개발자들이 모듈화,
유지보수 가능성 및 확장 가능성이 높은 코드를 작성할 수 있도록 지침을 제공할 수 있다.
이러한 원칙을 따르면 개발자들은 시간이 지나도 이해하기 쉽고 테스트 및 수정하기 쉬운 코드를 작성하도록 도와준다.
그렇다면 SOLID 원칙(SOLID Principle)이 무엇을 뜻하는가?
SOLID 원칙 (Principle)
SOLID Principle은 5가지 원칙을 의미한다.
부연설명과 파이썬 (Python3)로 설명하면 다음과 같다
- SRP(Single Responsibility Principle)
- 클래스가 변경되어야 할 이유가 단 하나뿐이어야 한다는 것을 의미한다. 다시 말해, 클래스는 단 하나의 책임만을 가져야 하며, 하나의 작업이나 역할만을 수행해야 한다. 이 원칙은 코드의 유지보수와 재사용성을 높이는 데 도움이 된다.# 나쁜 예제 - SRP 위반 class Employee: def __init__(self, name, address): self.name = name self.address = address def save_employee(self): # employee를 리포트를 프린트 하는 코드 pass def print_employee_report(self): # employee를 리포트를 프린트 하는 코드 # code for printing employee report pass # 좋은 예제 - SRP 준수 class Employee: def __init__(self, name, address): self.name = name self.address = address class EmployeeSaver: def save_employee(self, employee): # employee를 리포트를 프린트 하는 코드 pass class EmployeeReportPrinter: def print_employee_report(self, employee): # employee를 리포트를 프린트 하는 코드 pass
- OCP(Open/Closed Principle)
- 클래스는 확장에는 개방되어 있어야 하지만 수정에는 폐쇄되어야 한다는 것을 의미한다. 즉, 기존 코드를 수정하지 않고도 클래스의 기능을 확장할 수 있어야 한다. 이 원칙의 아이디어는 이미 검증되고 테스트된 기존 코드를 수정하지 않고도 기능을 확장하는 것을 말한다.# 나쁜 예제 - OCP 위반 class Shape: def __init__(self, name): self.name = name def draw(self): pass class Circle(Shape): def draw(self): # 원형을 그리는 코드 pass class Square(Shape): def draw(self): # 사각형을 그리는 코드 pass # 새로운 도형을 추가하기 위해 Shape클래스를 수정해야 한다. class Triangle(Shape): def draw(self): # 삼각형을 그리는 코드 pass # 좋은 예제 - OCP 즌스 class Shape: def __init__(self, name): self.name = name def draw(self): pass class Circle(Shape): def draw(self): # 원형을 그리는 코드 pass class Square(Shape): def draw(self): # 사각형을 그리는 코드 pass class Triangle(Shape): def draw(self): # 삼각형을 그리는 코드 pass class ShapeDrawer: def __init__(self, shapes): self.shapes = shapes def draw_all(self): for shape in self.shapes: shape.draw()
- LSP(Liskov Substitution Principle)
- 파생 클래스의 객체가 기본 클래스의 객체를 대체할 수 있어야 하며, 즉 클래스 계층 구조가 있을 경우, 파생 클래스의 동작은 기본 클래스와 다르지 않아야 한다. 부모클래스가 들어갈 자리에 자식 클래스를 넣어도 계획대로 잘 작동해야 한다.# 나쁜 예제 - LSP 위반 class Rectangle: def __init__(self, width, height): self.width = width self.height = height def set_width(self, width): self.width = width def set_height(self, height): self.height = height def area(self): return self.width * self.height class Square(Rectangle): def __init__(self, size): self.width = size self.height = size def set_width(self, width): self.width = width self.height = width def set_height(self, height): self.width = height self.height = height # LSP를 위반하는 코드 def calculate_area(rectangle): rectangle.set_width(5) rectangle.set_height(4) assert rectangle.area() == 20 calculate_area(Square(10)) # 좋은 예제 - LSP 준수 class Shape: def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height class Square(Shape): def __init__(self, size): self.size = size def area(self): return self.size ** 2 # 사각형을 포함해서 어떤 도형도 그릴수 있다. def calculate_area(shape): assert shape.area() == 20
- ISP(Interface Segregation Principle)
- 클라이언트가 사용하지 않는 메서드에 의존하지 않아야 한다는 것을 의미한다. 이것은 큰 인터페이스를 더 작고 구체적인 것으로 분할하여 클라이언트가 실제로 필요한 메서드에만 의존할 수 있도록 하는 것을 의미한다. 코드의 복잡성을 줄이고 재사용성을 향상하는 데 도움이 된다.# 나쁜 예제 - ISP 위반 class Printer: def print(self, document): pass class Scanner: def scan(self, document): pass class Fax: def fax(self, document): pass # 이클래스는 다른 불필요한 메소드를 강요한다. class AllInOnePrinter(Printer, Scanner, Fax): pass # 좋은 예제 - ISP 준수 class Printer: def print(self, document): pass class Scanner: def scan(self, document): pass class Fax: def fax(self, document): pass class Photocopier(Printer, Scanner): pass class MultifunctionalDevice(Printer, Scanner, Fax): pass
- DIP(Dependency Inversion Principle)
- 상위클래스는 하위클래스에 의존해서는 안된다는 것을 의미한다. 하위클래스가 상위클래스에 의존을 해야 하며 상위클래스가 하위클래스에 의존하는 건은 가능하지 않아야 하는 것을 말해준다. 코드를 더 유연하고 변경하기 쉽게 만드는 데 도움이 됩니다.# 나쁜 예제 - DIP 위반 class EmailService: def send_email(self, message): # 이멜을 보내는 코드 pass class UserService: def __init__(self): self.email_service = EmailService() def create_user(self, name, email): # 유저를 만드는 코드 self.email_service.send_email(f"Welcome {name}!") # 좋은 예제 - DIP 준수 class EmailService: def send_email(self, message): # 이멜을 보내는 코드 pass class UserService: def __init__(self, email_service): self.email_service = email_service def create_user(self, name, email): # 유저를 만드는 코드 self.email_service.send_email(f"Welcome {name}!")
예전 회사에서 이 원칙으로 코딩을 짜는 습관을 갖게 되었지만,
실질적으로 프로그래밍을 할 땐
이러한 원칙을 완전히 준수하는 것은 상당히 어렵고 시간도 상당히 할애해야 하는 것도 필요하다.
단지 개발자들이 상황에 맞게 유연하게 적용하는 점이 필요한 것 같다.
OOP와 밀접한 관련이 있는 SOLID 원칙을 준수하여,
좀 더 나은 코드를 작성하며 코드 유지보수 및 확장을 조금 더 쉽게 할 수 있을 것이다.
구직 중 인터뷰를 하다 보면 잠깐씩 나오는 질문들 중에 가끔 나왔었다.
SOLID 원칙과 OOP의 연관성을 두고 조금 알아 두는 것도 좋지 않을까 싶다.
'잡인터뷰' 카테고리의 다른 글
지속적인 통합, 지속적인 배포 (Continuous Integration/Continuous Delivery - CI/CD) (0) | 2023.03.13 |
---|---|
바이너리 서치 - Binary Search (2) | 2023.03.12 |
잡 인터뷰 - 투포인터 알고리즘(Two Pointer Approach) (4) | 2023.03.10 |
풀스택 개발자란? - What is Full Stack Developer (0) | 2023.03.10 |
잡 인터뷰 - OAuth 1.0 과 OAuth 2.0의 차이점 (0) | 2023.03.07 |