실체 대상 설계 원칙

28778 단어 rubyoop
실체 디자인 원칙은 대상을 대상으로 하는 디자인에서 디자인과 구조류에 사용되는 다섯 가지 관건적인 원칙이다.그것들은 이해하기 쉽고, 협업, 확장, 유지보수하기 쉬운 유효한 코드를 작성하는 데 필요한 규칙이다.

왜 그것들은 매우 중요합니까?


우리는 이 문제에 하나의 용례로 대답할 것이다. 우리는 다섯 가지 원칙 중에서 '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에서 나/나의 여정을 따라갈 수 있다.안녕히 계세요!

    좋은 웹페이지 즐겨찾기