실체 대상 설계 원칙
왜 그것들은 매우 중요합니까?
우리는 이 문제에 하나의 용례로 대답할 것이다. 우리는 다섯 가지 원칙 중에서 'O-개-폐 원칙 (확장은 개방하지만 수정은 폐쇄)' 을 추출할 것이다.
새로운 기능이나 모듈을 추가하려면 클래스를 수정해야 하지 않습니까?네, 그렇지만 사실은 아니에요. 현재 생산 중인 테스트 코드를 변경하거나 수정하지 않는 것을 권장합니다.이것은 일부 부작용을 초래하고 전체 종류의 기능을 파괴할 수 있으므로 더욱 재구성해야 한다(아! 작업!).그래서 우리는 확장에 대해 항상 개방하고 수정에 대해 닫아야 한다는 방식으로 그것을 구축해야 한다.
이것이 구성보다는 Ruby on Rails가 제공하는 약속입니다.'헤이, 우리 자신을 데리고 가는 것이 아니라 유사한 관례와 디자인 모델을 따르자'고 적혀 있다.우리가 현재 가지고 있는 높은 협업의 개발 환경에서 나에게 좋은 것은 다른 사람에게 좋지 않을 수도 있다.더 이상 시간을 낭비하지 말고 코드를 쓰기 시작합시다.
각 원칙에 대해 더 자세히 살펴보겠습니다. 저는 루비 프로그래밍 언어를 예로 들겠습니다.이러한 개념은 대상을 대상으로 하는 모든 프로그래밍 언어에 적용된다.
그럼, 이것은 누가 제정한 것입니까?배경.
SOLID principles의 이론은 Robert C.Martin(일명 Bob 아저씨)이 2000년paper의 에서 도입한 것으로 SOLID의 알파벳 줄임말은 나중에 마이클 Feathers가 도입했다.
다섯 가지 원칙은 다음과 같다.
모든 디자인 원칙의 주요 목표는'이해할 수 있고 읽을 수 있으며 테스트할 수 있는 코드를 만들고 많은 개발자들이 협동하여 일을 할 수 있다'는 것이다.
단일 책임 원칙
말 그대로 단일 책임 원칙은 하나의 유형에 하나의 책임만 있어야 한다는 것을 의미한다.
사용자에게 매주 분석을 보내는 SaaS 제품을 고려하십시오.이 과정을 완성하려면 두 가지 행동을 취해야 한다.하나는 보고서 생성 자체이고, 다른 하나는 보고서를 보내는 것이다.만약 우리가 그들에게 이메일을 보내고 있다면.
우리가 위반한 장면을 보고 단일 책임 원칙을 따르는 예를 들자.
# Violation of the Single Responsibility Principle in Ruby
class WeeklyAnalyticsMailer
def initialize(activities, user)
@activities = activities
@user = user
@report = ''
end
def generate_report!
# Generate Report
end
def send_report
Mail.deliver(
from: '[email protected]',
to: @user.email,
subject: 'Yo! Your weekly analytics is here.',
body: @report
)
end
end
mailer = WeeklyAnalytics.new(user)
mailer.generate_report!
mailer.send_report
분석 메일을 보내는 것은 하나의 동작처럼 보이지만, 그것은 두 가지 다른 하위 동작과 관련이 있다.왜 상술한 a류는 단일 책임 원칙을 위반했습니까?이 반에는 두 가지 직책이 있는데, 하나는 보고서를 작성하는 것이고, 다른 하나는 그들에게 이메일을 보내는 것이다.클래스 이름을 Weekly Analytics Mailer로 지정해야 하며, 예상을 초과한 추가 작업을 해서는 안 됩니다.이것은 분명히 원칙을 위반했다.어떻게 이 문제를 해결합니까?우리는 두 가지 다른 종류를 구축할 것이다. 그 중 하나는 보고서를 만들고, 다른 하나는 사용자에게 이메일을 보낼 것이다.
# Correct use of the Single Responsibility Principle in Ruby
class WeeklyAnalyticsMailer
def initialize(report, user)
@report = report
@user = user
end
def deliver
Mail.deliver(
from: '[email protected]',
to: @user.email,
subject: 'Yo! Your weekly analytics is here.',
body: @report
)
end
end
class WeeklyAnalyticsGenerator
def initialize(activities)
@activities = activities
end
def generate
# Generate Report
end
end
report = WeeklyAnalyticsGenerator.new(activities).generate
WeeklyAnalyticsMailer.new(report, user).deliver
계획에 따르면, 우리는 두 개의 반이 있는데, 각 반은 모두 자신의 직책을 가지고 있으며, 한 반을 초과하지 않을 것이다.만일 우리가 메일 클래스의 기능을 확장하고 싶다면, (만약 우리가 SendGrid를 사용하여 전자메일을 보낸다고 가정한다면, 우리는 전용 메일 클래스에 대해 필요한 변경만 하고,generator 클래스에 손대지 않아도 된다.개폐 원리
우리는 안내문에서 개폐 원리를 간략하게 소개했다.우리는 지금 더 많은 것을 알게 될 것이다.
이 원칙의 주요 목표는 생산 중인 기존 원본 코드를 변경하거나 재구성하는 것이 아니라 응용 프로그램의 기능을 확장하는 유연한 시스템 구조를 만드는 것이다.
객체 또는 엔티티는 확장을 위해 열려 있지만 수정을 위해 닫혀 있어야 합니다.
예를 들어 분석을 다시 보내야 한다고 가정합시다
서로 다른 형식과 매개체.
# Violation of the Open-Closed Principle in Ruby
class Analytics
def initialize(user, activities, type, medium)
@user = user
@activities = activities
@type = type
@medium = medium
end
def send
deliver generate
end
private
def deliver(report)
case @type
when :email
# Send Report via Email
else
raise NotImplementedError
end
end
def generate
case @type
when :csv
# Generate CSV report
when :pdf
# Generate PDF report
else
raise NotImplementedError
end
end
end
report = Analytics.new(
user,
activities,
:csv,
:email
)
report.send
위의 예에서 CSV/PDF를 이메일로 보낼 수 있습니다.만약 우리가 원시 형식과 새로운 미디어 문자 메시지 같은 새로운 형식을 추가하고 싶다면, 우리는 코드를 수정해야 한다. 이것은 분명히 우리의 개방-폐쇄 원칙을 위반한 것이다.우리는 상술한 코드를 재구성하여 개폐 원칙을 따를 것이다.# Correct use of the Open-Closed Principle in Ruby
class Analytics
def initialize(medium, type)
@medium = medium
@type = type
end
def send
@medium.deliver @type.generate
end
end
class EmailMedium
def initialize(user)
@user = user
# ... Setup Email medium
end
def deliver
# Deliver Email
end
end
class SmsMedium
def initialize(user)
@user = user
# ... Setup SMS medium
end
def deliver
# Deliver SMS
end
end
class PdfGenerator
def initialize(activities)
@activities = activities
end
def generate
# Generate PDF Report
end
end
class CsvGenerator
def initialize(activities)
@activities = activities
end
def generate
# Generate CSV Report
end
end
class RawTextGenerator
def initialize(activities)
@activities = activities
end
def generate
# Generate Raw Report
end
end
report = Analytics.new(
SmsMedium.new(user),
RawTextGenerator.new(activities)
)
report.send
우리는 상술한 종류를 재구성하여 모듈을 쉽게 확장할 수 있고 기존 코드를 변경할 필요가 없으며, 기존 코드는 현재 개폐 원칙을 따르고 있다.잘 됐다!이씨대환원칙
"하위 클래스는 기본 클래스에 추가해야 하며, 그것을 바꾸는 것이 아니라 기본 클래스에 추가해야 한다."
간단한 foo-bar의 예는 모든 정사각형은 직사각형이고 반대로도 그렇다는 것이다.만약 우리가 직사각형류를 정사각형류의 기류로 삼는다면그리고 우리는 모든 정사각형이 직사각형이기 때문에 길이와 너비를 같은 값으로 전달하여 정사각형의 면적을 계산할 것이다.우리는 정확한 값을 얻을 수 있지만, 만약 우리가 반대로 한다면, 그것은 잘못된 값을 초래할 것이다.
이 문제를 극복하기 위해서, 우리는 Shape류를 우리의 기류로 삼아야 한다. 직사각형과 정사각형은 Liskov 교체 원리를 만족시키는 기류인 Shape에서 확장될 것이다.foobar의 예는 이미 충분하다.우리는 더욱 현실적인 예를 볼 것이다.
일반적으로 Liskov 교체 원칙에 따르면 부모 실례는 그 하위 실례 중 하나로 대체할 수 있어야 하며 어떠한 의외의 행위나 부정확한 행위도 발생하지 않는다.따라서 LSP는 추상이 정확하다는 것을 확보하고 개발자가 더 많은 리셋 가능한 코드와 클래스 차원 구조를 잘 조직하도록 돕는다.
원칙에 어긋나는 예를 하나 봅시다.모든 청구서를 검색하는 방법이 있는 UserInvoice라는 기본 클래스가 있습니다.
같은 방법
invoices
을 가진 기본 클래스를 상속하는 하위 클래스 AdminInvoice가 있습니다.AdminInvoice는 객체 배열의 기본 클래스를 반환하는 방법에 비해 문자열을 반환합니다.이것은 분명히 LSP를 위반한 것이다. 왜냐하면 하위 클래스는 기본 클래스로 대체될 수 없고 부작용이 발생하지 않기 때문이다. 왜냐하면 하위 클래스가 방법을 덮어쓰는 행위이기 때문이다.# Violation of the Liskov Substitution Principle in Ruby
class UserInvoice
def initialize(user)
@user = user
end
def invoices
@user.invoices
end
end
class AdminInvoice < UserInvoice
def invoices
invoices = super
string = ''
user_invoices.each do |invoice|
string += "Date: #{invoice.date} Amount: #{invoice.amount} Remarks: #{invoice.remarks}\n"
end
string
end
end
이 문제를 해결하기 위해서, 우리는 포맷된 하위 클래스를 처리하는 데 새로운 포맷 방법을 도입해야 한다.그 후에 하위 클래스는 기본 클래스와 교환할 수 있고 부작용이 없기 때문에 LSP를 만족시킬 수 있다.# Correct use of the Liskov Substitution Principle in Ruby
class UserInvoice
def initialize(user)
@user = user
end
def invoices
@user.invoices
end
end
class AdminInvoice < UserInvoice
def invoices
super
end
def formatted_invoices
string = ''
invoices.each do |invoice|
string += "Date: #{invoice.date} Amount: #{invoice.amount} Remarks: #{invoice.remarks}\n"
end
string
end
end
인터페이스 분리 원칙
ISP는 "클라이언트는 그들이 사용하지 않는 방법에 의존해서는 안 된다"며 "클라이언트에 특정한 몇 개의 인터페이스가 일반적인 인터페이스보다 낫다"고 말했다.
이 원칙은 비만인을 다른 사람으로 나누는 데 중심을 두고 있다.만약 우리가 ATM기를 가지고 있다면, 그것은 네 가지 조작을 수행할 수 있다. 그것이 바로 로그인, 인출, 잔액, 충전이다.
# Violation of the Interface Segregation Principle in Ruby
class ATMInterface
def login
end
def withdraw(amount)
# Cash withdraw logic
end
def balance
# Account balance logic
end
def fill(amount)
# Fill cash (Done by the ATM Custodian)
end
end
class User
def initialize
@atm_machine = ATMInterface.new
end
def transact
@atm_machine.login
@atm_machine.withdraw(500)
@atm_machine.balance
end
end
class Custodian
def initialize
@atm_machine = ATMInterface.new
end
def load
@atm_machine.login
@atm_machine.fill(5000)
end
end
우리는 두 가지 유형의 사용자 사용자와 보관인이 있는데 그 중에서 사용자는 3개의 조작(로그인, 인출, 결산)을 사용하고 보관자는 2개의 조작(로그인과 기입)을 사용한다.우리는 고객이 그것들을 필요로 하지 않아도 (예를 들어 사용자가 보충할 필요가 없고 보관자가 잔액을 추출/검사할 필요가 없다) ATMinterface라는 클래스를 가지고 있다.이것은 당연히 우리의 ISP를 위반한 것이다.상술한 지방을 서로 다른 자류로 분류합시다.
# Correct use of the Interface Segregation Principle in Ruby
class ATMInterface
def login
end
end
class ATMUserInterface < ATMInterface
def withdraw(amount)
# Cash withdraw logic
end
def balance
# Account balance logic
end
end
class ATMCustodianInterface < ATMInterface
def replenish
# Fill cash (Done by the ATM Custodian)
end
end
class User
def initialize
@atm_machine = ATMUserInterface.new
end
def transact
@atm_machine.login
@atm_machine.withdraw(500)
@atm_machine.balance
end
end
class Custodian
def initialize
@atm_machine = ATMCustodianInterface.new
end
def load
@atm_machine.login
@atm_machine.replenish
end
end
역치원칙에 의존하다
"고급 모듈은 저급 모듈에 의존해서는 안 된다. 두 모듈 모두 추상에 의존해야 한다. 또한 추상은 세부 사항에 의존해서는 안 된다. 세부 사항은 추상에 의존해야 한다."
이 네 마디는 너무 상세하다.무슨 뜻인지 한번 봅시다.밥 아저씨의 말에 따르면 경사는 다른 두 가지 튼튼한 원칙을 엄격히 따르는 결과이다. 그것이 바로 개폐와 Liskov 교체 원칙이다.따라서 이것은 뚜렷하게 다른 추상이 있을 것이다.
그것은 또한 읽을 수 있고 확장할 수 있어야 하며, 하위 클래스는 기본 클래스의 다른 실례로 쉽게 바뀌어야 하며, 시스템을 파괴하지 않을 것이다.
# Violation of the Dependency Inversion Principle in Ruby
class Parser
def parse_xml(file)
XmlParser.new.parse(file)
end
def parse_csv(file)
CsvParser.new.parse(file)
end
end
class XmlParser
def parse(file)
# parse xml
end
end
class CsvParser
def parse(file)
# parse csv
end
end
클래스 해석기는 추상적이지 않고 클래스 XmlParser와 CsvParser에 의존한다. 이것은 클래스 XmlParser와 CsvParser가 다른 클래스를 인용하는 논리를 포함할 수 있기 때문에 DIP 원칙을 위반한 것이다.따라서, 클래스 해석기를 수정할 때, 우리는 모든 관련 클래스에 영향을 줄 수 있습니다.#Correct use of the Dependency Inversion Principle in Ruby
class Parser
def initialize(parser: CsvParser.new)
@parser = parser
end
def parse(file)
@parser.parse(file)
end
end
class XmlParser
def parse(file)
# parse xml
end
end
class CsvParser
def parse(file)
# parse csv
end
end
결론
단일한 방정식이나 규칙이 없다는 것을 명심해라.그러나 미리 정의된 규칙을 정확하게 따르면 위대한 디자인이 만들어질 것이다.깨끗한 코드를 작성하는 데는 경험이 필요하다. 이런 원칙을 교묘하게 사용하면 더욱 좋은 결과를 얻을 수 있다. 이러한 결과는 확장 가능하고 유지보수적이며 모든 사람의 생활을 더욱 가볍게 할 수 있다.
나는 왜 이 문장을 썼습니까?
나는 이미 나의 직업 도로를 다른 부분으로 계획했다.내가 컴퓨터 과학 학위를 받은 후, 나는 나를 흥분시키는 어떤 기술도 탐색하기 시작했다.JS에서 Rust까지 백엔드와 프런트엔드를 포함합니다.응, 나는 이미 탐색해 봤어. 나를 흥분시키는 어떤 것도 쉽게 찾아서 이해하고 사용할 수 있어.다음은요?장악
너는 각각/Github에서 나/나의 여정을 따라갈 수 있다.안녕히 계세요!
Reference
이 문제에 관하여(실체 대상 설계 원칙), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/sathish/the-solid-object-oriented-design-principles-4e7o텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)