Những cách thực tế để thực hiện SRP là gì?


11

Đơn giản là những kỹ thuật thực tế mà mọi người sử dụng để kiểm tra xem một lớp có vi phạm nguyên tắc trách nhiệm duy nhất không?

Tôi biết rằng một lớp chỉ nên có một lý do để thay đổi, nhưng câu đó hơi thiếu một cách thực tế để thực sự thực hiện điều đó.

Cách duy nhất tôi tìm thấy là sử dụng câu "Bản thân ......... nên .........." trong đó không gian đầu tiên là tên lớp và sau là tên phương thức (trách nhiệm).

Tuy nhiên, đôi khi thật khó để biết liệu một trách nhiệm có thực sự vi phạm SRP hay không.

Có nhiều cách để kiểm tra SRP không?

Ghi chú:

Câu hỏi không phải là về ý nghĩa của SRP, mà là một phương pháp thực tế hoặc một loạt các bước để kiểm tra và thực hiện SRP.

CẬP NHẬT

Lớp báo cáo

Tôi đã thêm một lớp mẫu rõ ràng vi phạm SRP. Sẽ thật tuyệt nếu mọi người có thể sử dụng nó như một ví dụ để giải thích cách họ tiếp cận nguyên tắc trách nhiệm duy nhất.

Ví dụ là từ đây .


Đây là một quy tắc thú vị, nhưng bạn vẫn có thể viết: "Một lớp người có thể tự kết xuất". Điều này có thể được coi là vi phạm đối với SRP, vì bao gồm GUI trong cùng một lớp có chứa các quy tắc kinh doanh và sự kiên trì dữ liệu là không ổn. Vì vậy, tôi nghĩ rằng bạn cần thêm khái niệm về miền kiến ​​trúc (tầng và lớp) và đảm bảo rằng tuyên bố này chỉ hợp lệ với 1 trong số các tên miền đó (chẳng hạn như GUI, Truy cập dữ liệu, v.v.)
NoChance

@EmmadKareem Quy tắc này đã được đề cập trong Phân tích và Thiết kế hướng đối tượng đầu tiên và đó chính xác là những gì tôi nghĩ về nó. Đó là một phần thiếu một cách thực tế để thực hiện nó. Họ đã đề cập rằng đôi khi các trách nhiệm sẽ không rõ ràng đối với nhà thiết kế và anh ta phải sử dụng rất nhiều ý nghĩa thông thường để đánh giá liệu phương pháp đó có thực sự nằm trong lớp này hay không.
Songo

Nếu bạn thực sự muốn hiểu SRP, hãy đọc một số tác phẩm của chú Bob Martin. Mã của anh ấy là một trong những thứ đẹp nhất tôi từng thấy, và tôi tin rằng bất cứ điều gì anh ấy nói về SRP không chỉ là lời khuyên âm thanh, mà còn hơn cả việc vẫy tay.
Robert Harvey

Và những người bỏ phiếu xin vui lòng giải thích lý do để cải thiện bài viết?!
Songo

Câu trả lời:


7

SRP tuyên bố, không có gì chắc chắn, rằng một lớp chỉ nên có một lý do để thay đổi.

Giải mã lớp "báo cáo" trong câu hỏi, nó có ba phương thức:

  • printReport
  • getReportData
  • formatReport

Bỏ qua sự dư thừa Reportđang được sử dụng trong mọi phương thức, thật dễ dàng để biết lý do tại sao điều này vi phạm SRP:

  • Thuật ngữ "in" ngụ ý một số loại UI hoặc máy in thực tế. Do đó, lớp này chứa một số lượng UI hoặc logic trình bày. Một thay đổi đối với các yêu cầu UI sẽ đòi hỏi phải thay đổi Reportlớp.

  • Thuật ngữ "dữ liệu" ngụ ý một cấu trúc dữ liệu thuộc loại nào đó, nhưng không thực sự chỉ định cái gì (XML? JSON? CSV?). Bất kể, nếu "nội dung" của báo cáo từng thay đổi, thì phương pháp này cũng sẽ như vậy. Có khớp nối với cơ sở dữ liệu hoặc tên miền.

  • formatReportnói chung chỉ là một cái tên khủng khiếp cho một phương thức, nhưng tôi cho rằng bằng cách nhìn vào nó một lần nữa nó có liên quan đến UI và có lẽ là một khía cạnh khác của UI hơn printReport. Vì vậy, một lý do khác, không liên quan để thay đổi.

Vì vậy, một lớp này có thể được kết hợp với cơ sở dữ liệu, thiết bị màn hình / máy in một số logic định dạng bên trong cho nhật ký hoặc đầu ra tệp hoặc không có gì. Bằng cách có tất cả ba hàm trong một lớp, bạn sẽ nhân số lượng phụ thuộc và tăng gấp ba xác suất rằng bất kỳ thay đổi phụ thuộc hoặc yêu cầu nào sẽ phá vỡ lớp này (hoặc một cái gì khác phụ thuộc vào nó).

Một phần của vấn đề ở đây là bạn đã chọn một ví dụ đặc biệt gai góc. Bạn nên có lẽ không có một lớp gọi là Report, ngay cả khi nó chỉ thực hiện một điều , bởi vì ... những gì báo cáo? Không phải tất cả "báo cáo" các con thú hoàn toàn khác nhau, dựa trên dữ liệu khác nhau và các yêu cầu khác nhau? Và không phải là một báo cáo một cái gì đó đã được định dạng, cho màn hình hoặc để in?

Nhưng, nhìn qua điều đó và tạo nên một cái tên cụ thể giả định - hãy gọi nó IncomeStatement(một báo cáo rất phổ biến) - một kiến ​​trúc "SRPed" thích hợp sẽ có ba loại:

  • IncomeStatement- tên miền và / hoặc lớp mô hình có chứa và / hoặc tính toán thông tin xuất hiện trên các báo cáo được định dạng.

  • IncomeStatementPrinter, mà có lẽ sẽ thực hiện một số giao diện tiêu chuẩn như IPrintable<T>. Có một phương thức chính Print(IncomeStatement)và có thể một số phương thức hoặc thuộc tính khác để định cấu hình cài đặt dành riêng cho in.

  • IncomeStatementRenderer, xử lý kết xuất màn hình và rất giống với lớp máy in.

  • Cuối cùng, bạn cũng có thể thêm các lớp đặc trưng hơn như IncomeStatementExporter/ IExportable<TReport, TFormat>.

Điều này được thực hiện dễ dàng hơn đáng kể trong các ngôn ngữ hiện đại với sự ra đời của generic và container IoC. Hầu hết mã ứng dụng của bạn không cần dựa vào IncomeStatementPrinterlớp cụ thể , nó có thể sử dụng IPrintable<T>và do đó hoạt động trên bất kỳ loại báo cáo có thể in nào, cung cấp cho bạn tất cả các lợi ích cảm nhận của Reportlớp cơ sở với một printphương thức và không có vi phạm SRP thông thường nào . Việc thực hiện thực tế chỉ cần được khai báo một lần, trong đăng ký container IoC.

Một số người, khi đối mặt với thiết kế trên, đã trả lời với một cái gì đó như: "nhưng điều này trông giống như mã thủ tục và toàn bộ quan điểm của OOP là giúp chúng tôi thoát khỏi sự phân tách dữ liệu và hành vi!" Để tôi nói: sai .

Các IncomeStatementkhông chỉ là "dữ liệu", và sai lầm nói trên là nguyên nhân gây ra rất nhiều folks OOP cảm thấy họ đang làm điều gì đó sai bằng cách tạo ra một lớp "trong suốt" như vậy và sau đó bắt đầu gây nhiễu các loại chức năng không liên quan vào IncomeStatement(tốt, mà và sự lười biếng nói chung). Lớp này có thể bắt đầu chỉ là dữ liệu, nhưng theo thời gian, được đảm bảo, nó sẽ kết thúc giống như một mô hình .

Ví dụ: báo cáo thu nhập thực tế có tổng doanh thu , tổng chi phí và dòng thu nhập ròng . Một hệ thống tài chính được thiết kế hợp lý rất có thể sẽ không lưu trữ những thứ này vì chúng không phải là dữ liệu giao dịch - thực tế, chúng thay đổi dựa trên việc bổ sung dữ liệu giao dịch mới. Tuy nhiên, việc tính toán các dòng này sẽ luôn giống hệt nhau, bất kể bạn đang in, kết xuất hoặc xuất báo cáo. Vì vậy, bạn IncomeStatementlớp sẽ có một số lượng hợp lý của hành vi để nó trong hình thức getTotalRevenues(), getTotalExpenses()getNetIncome()phương pháp, và có lẽ một số người khác. Nó là một đối tượng kiểu OOP chính hãng với hành vi của chính nó, ngay cả khi nó không thực sự "làm" nhiều.

Nhưng formatprintphương pháp, chúng không liên quan gì đến thông tin. Trên thực tế, không quá khó để bạn có thể thực hiện một số phương pháp này, ví dụ như một tuyên bố chi tiết về quản lý và một tuyên bố không chi tiết cho các cổ đông. Việc tách các hàm độc lập này thành các lớp khác nhau cho bạn khả năng chọn các triển khai khác nhau trong thời gian chạy mà không phải chịu gánh nặng của một print(bool includeDetails, bool includeSubtotals, bool includeTotals, int columnWidth, CompanyLetterhead letterhead, ...)phương thức phù hợp với một kích thước . Kinh quá!

Hy vọng rằng bạn có thể thấy phương pháp tham số ồ ạt ở trên sai ở đâu và phương thức triển khai riêng biệt đi đúng; trong trường hợp một đối tượng, mỗi khi bạn thêm một nếp nhăn mới vào logic in, bạn phải thay đổi mô hình miền của mình ( Tim in finance muốn số trang, nhưng chỉ trên báo cáo nội bộ, bạn có thể thêm điều đó không? ) thay vào đó chỉ cần thêm một thuộc tính cấu hình cho một hoặc hai lớp vệ tinh.

Thực hiện SRP đúng cách là quản lý các phụ thuộc . Tóm lại, nếu một lớp đã làm một cái gì đó hữu ích và bạn đang xem xét thêm một phương thức khác sẽ giới thiệu một phụ thuộc mới (như UI, máy in, mạng, tệp, bất cứ thứ gì), thì không . Thay vào đó, hãy suy nghĩ về cách bạn có thể thêm chức năng này vào một lớp mới và cách bạn có thể làm cho lớp mới này phù hợp với kiến ​​trúc tổng thể của bạn (khá dễ dàng khi bạn thiết kế xung quanh việc tiêm phụ thuộc). Đó là nguyên tắc / quy trình chung.


Lưu ý bên lề: Giống như Robert, tôi từ chối một cách kiên quyết khái niệm rằng một lớp tuân thủ SRP chỉ nên có một hoặc hai biến trạng thái. Một lớp bọc mỏng như vậy hiếm khi có thể được dự kiến ​​sẽ làm bất cứ điều gì thực sự hữu ích. Vì vậy, đừng quá nhiệt tình với điều này.


+1 câu trả lời tuyệt vời thực sự. Tuy nhiên, tôi chỉ bối rối về lớp học IncomeStatement. Liệu thiết kế đề xuất của bạn có nghĩa rằng IncomeStatementsẽ có các trường hợp IncomeStatementPrinter& IncomeStatementRendererdo đó khi tôi gọi print()vào IncomeStatementnó sẽ ủy cuộc gọi đến IncomeStatementPrinterđể thay thế?
Songo

@Songo: Hoàn toàn không! Bạn không nên có sự phụ thuộc theo chu kỳ nếu bạn đang theo dõi RẮN. Rõ ràng câu trả lời của tôi đã không làm cho nó rõ ràng đủ để IncomeStatementlớp không có một printphương pháp, hoặc một formatphương pháp, hoặc bất kỳ phương pháp khác mà không đối phó trực tiếp với kiểm tra hoặc thao tác dữ liệu báo cáo riêng của mình. Đó là những gì các lớp khác dành cho. Nếu bạn muốn in một cái, thì bạn phải phụ thuộc vào IPrintable<IncomeStatement>giao diện được đăng ký trong vùng chứa.
Aaronaught

aah tôi thấy quan điểm của bạn Tuy nhiên, sự phụ thuộc theo chu kỳ ở đâu nếu tôi tiêm một Printerthể hiện trong IncomeStatementlớp? theo cách tôi tưởng tượng là khi tôi gọi IncomeStatement.print()nó sẽ ủy thác cho nó IncomeStatementPrinter.print(this, format). Cách tiếp cận này có gì sai? ... Một câu hỏi khác, Bạn đã đề cập rằng IncomeStatementnên chứa thông tin xuất hiện trên các báo cáo được định dạng nếu tôi muốn nó được đọc từ cơ sở dữ liệu hoặc từ tệp XML, tôi nên trích xuất phương thức tải dữ liệu vào một lớp riêng và ủy thác cuộc gọi cho nó trong IncomeStatement?
Songo

@Songo: Bạn có IncomeStatementPrintertùy thuộc IncomeStatementIncomeStatementtùy thuộc vào IncomeStatementPrinter. Đó là một sự phụ thuộc theo chu kỳ. Và đó chỉ là thiết kế tồi; không có lý do nào IncomeStatementđể biết bất cứ điều gì về một Printerhoặc IncomeStatementPrinter- đó là một mô hình miền, nó không liên quan đến in ấn và phái đoàn là vô nghĩa vì bất kỳ lớp nào khác có thể tạo hoặc có được một IncomeStatementPrinter. Không có lý do chính đáng để có bất kỳ khái niệm in ấn nào trong mô hình miền.
Aaronaught

Đối với cách bạn tải IncomeStatementtừ cơ sở dữ liệu (hoặc tệp XML) - thông thường, được xử lý bởi kho lưu trữ và / hoặc trình ánh xạ, không phải miền và một lần nữa, bạn không ủy quyền điều này trong miền; nếu một số lớp khác cần đọc một trong các mô hình này thì nó yêu cầu kho lưu trữ đó rõ ràng . Trừ khi bạn đang triển khai mẫu Bản ghi hoạt động tôi đoán, nhưng tôi thực sự không phải là người hâm mộ.
Aaronaught

2

Cách tôi kiểm tra SRP là kiểm tra mọi phương thức (trách nhiệm) của một lớp và đặt câu hỏi sau:

"Tôi có bao giờ cần thay đổi cách tôi thực hiện chức năng này không?"

Nếu tôi tìm thấy một chức năng mà tôi sẽ cần phải thực hiện theo các cách khác nhau (tùy thuộc vào một số loại cấu hình hoặc điều kiện) thì tôi biết chắc chắn rằng tôi cần một lớp bổ sung để xử lý trách nhiệm này.


1

Dưới đây là trích dẫn từ quy tắc 8 của Object Calisthenics :

Hầu hết các lớp nên đơn giản chịu trách nhiệm xử lý một biến trạng thái duy nhất, nhưng có một vài lớp sẽ yêu cầu hai biến. Thêm một biến đối tượng mới vào một lớp ngay lập tức làm giảm sự gắn kết của lớp đó. Nói chung, trong khi lập trình theo các quy tắc này, bạn sẽ thấy rằng có hai loại lớp, những loại duy trì trạng thái của một biến đối tượng duy nhất và các loại phối hợp hai biến riêng biệt. Nói chung, không trộn lẫn hai loại trách nhiệm

Với quan điểm này (hơi lý tưởng), bạn có thể nói rằng bất kỳ lớp nào chỉ chứa một hoặc hai biến trạng thái không có khả năng vi phạm SRP. Bạn cũng có thể nói rằng bất kỳ lớp nào chứa nhiều hơn hai biến trạng thái có thể vi phạm SRP.


2
Quan điểm này là vô vọng đơn giản. Ngay cả phương trình đơn giản nhưng nổi tiếng của Einstein cũng cần hai biến.
Robert Harvey

Câu hỏi của OP là "Có nhiều cách hơn để kiểm tra SRP không?" - đây là một chỉ số có thể Có, nó đơn giản và không giữ được trong mọi trường hợp, nhưng đó là một cách có thể để kiểm tra xem SRP có bị vi phạm hay không.
MattDavey

1
Tôi nghi ngờ trạng thái đột biến và bất biến cũng là một cân nhắc quan trọng
jk.

Quy tắc 8 mô tả quy trình hoàn hảo để tạo ra các thiết kế có hàng ngàn và hàng nghìn lớp làm cho hệ thống trở nên vô cùng phức tạp, không thể hiểu được và không thể nhầm lẫn. Nhưng mặt tích cực là bạn có thể theo dõi SRP.
Dunk

@Dunk Tôi không đồng ý với bạn, nhưng cuộc thảo luận đó hoàn toàn không có chủ đề cho câu hỏi.
MattDavey

1

Một triển khai có thể (trong Java). Tôi đã tự do với các loại trả lại nhưng trên tất cả tôi nghĩ rằng nó trả lời câu hỏi. TBH Tôi không nghĩ giao diện của lớp Báo cáo là xấu, mặc dù tên tốt hơn có thể theo thứ tự. Tôi bỏ qua các tuyên bố bảo vệ và khẳng định cho ngắn gọn.

EDIT: Cũng lưu ý rằng lớp học là bất biến. Vì vậy, một khi nó được tạo ra, bạn không thể thay đổi bất cứ điều gì. Bạn có thể thêm setFormatter () và setPrinter () và không gặp quá nhiều rắc rối. Chìa khóa, IMHO, là không thay đổi dữ liệu thô sau khi khởi tạo.

public class Report
{
    private ReportData data;
    private ReportDataDao dao;
    private ReportFormatter formatter;
    private ReportPrinter printer;


    /*
     *  Parameterized constructor for depndency injection, 
     *  there are better ways but this is explicit.
     */
    public Report(ReportDataDao dao, 
        ReportFormatter formatter, ReportPrinter printer)
    {
        super();
        this.dao = dao;
        this.formatter = formatter;
        this.printer = printer;
    }

    /*
     * Delegates to the injected printer.
     */
    public void printReport()
    {
        printer.print(formatReport());
    }


    /*
     * Lazy loading of data, delegates to the dao 
     * for the meat of the call.
     */
    public ReportData getReportData()
    {
        if (reportData == null)
        {
            reportData = dao.loadData();
        }
        return reportData;
    }

    /*
     * Delegate to the formatter for formatting 
     * (notice a pattern here).
     */
    public ReportData formatReport()
    {
        formatter.format(getReportData());
    }
}

Cảm ơn đã thực hiện. Tôi có 2 điều, trong dòng if (reportData == null)tôi đoán bạn có nghĩa là datathay thế. Thứ hai, tôi đã hy vọng biết làm thế nào bạn đến với việc thực hiện này. Giống như tại sao bạn quyết định ủy thác tất cả các cuộc gọi cho các đối tượng khác thay thế. Một điều nữa mà tôi luôn băn khoăn, đó có thực sự là trách nhiệm của một bản báo cáo không? Tại sao bạn không tạo một printerlớp riêng biệt có một hàm reporttạo?
Songo

Có, báo cáoData = dữ liệu, xin lỗi về điều đó. Phái đoàn cho phép kiểm soát chi tiết các phụ thuộc. Trong thời gian chạy, bạn có thể cung cấp các cài đặt thay thế cho từng thành phần. Bây giờ bạn có thể có một HtmlPrinter, PdfPrinter, JsonPrinter, ... vv Điều này cũng thuận tiện để kiểm tra vì bạn có thể kiểm tra các thành phần được ủy nhiệm của mình một cách tách biệt cũng như tích hợp trong đối tượng ở trên. Bạn chắc chắn có thể đảo ngược mối quan hệ giữa máy in và báo cáo, tôi chỉ muốn chứng minh rằng có thể cung cấp giải pháp với giao diện lớp được cung cấp. Đó là thói quen làm việc trên các hệ thống cũ. :)
Heath Lilley

hmmmm ... Vậy nếu bạn đang xây dựng hệ thống từ đầu, bạn sẽ chọn phương án nào? Một Printerlớp lấy một báo cáo hoặc một Reportlớp lấy một máy in? Tôi đã gặp một vấn đề tương tự trước khi tôi phải phân tích một báo cáo và tôi đã tranh luận với TL của mình nếu chúng ta nên xây dựng một trình phân tích cú pháp để báo cáo hoặc liệu báo cáo có nên có một trình phân tích cú pháp bên trong nó hay không và parse()cuộc gọi được ủy quyền cho nó.
Songo

Tôi sẽ làm cả ... máy in.print (báo cáo) để bắt đầu và báo cáo.print () nếu cần sau này. Điều tuyệt vời về cách tiếp cận máy in.print (báo cáo) là nó có khả năng tái sử dụng cao. Nó phân tách trách nhiệm và nó cho phép bạn có các phương pháp thuận tiện khi bạn cần chúng. Có thể bạn không muốn các đối tượng khác trong hệ thống của mình phải biết về ReportPrinter, do đó, bằng cách sử dụng phương thức print () trên một lớp, bạn sẽ đạt được mức độ tiết chế để cách ly logic in báo cáo của bạn với thế giới bên ngoài. Điều này vẫn có một vectơ thay đổi hẹp và dễ sử dụng.
Heath Lilley

0

Trong ví dụ của bạn, không rõ SRP đang bị vi phạm. Có lẽ báo cáo sẽ có thể định dạng và in chính nó, nếu chúng tương đối đơn giản:

class Report {
  void format() {
     text = text.trim();
  }

  void print() {
     new Printer().write(text);
  }
}

Các phương thức rất đơn giản, không có nghĩa là có ReportFormatterhoặc ReportPrintercác lớp. Vấn đề rõ ràng duy nhất trong giao diện là getReportDatado nó vi phạm, đừng hỏi về đối tượng không có giá trị.

Mặt khác, nếu các phương thức rất phức tạp hoặc có nhiều cách để định dạng hoặc in a Reportthì việc ủy ​​thác trách nhiệm (cũng dễ kiểm tra hơn):

class Report {
  void format(ReportFormatter formatter) {
     text = formatter.format(text);
  }

  void print(ReportPrinter printer) {
     printer.write(text);
  }
}

SRP là một nguyên tắc thiết kế không phải là một khái niệm triết học và vì vậy nó dựa trên mã thực tế mà bạn đang làm việc. Về mặt ngữ nghĩa, bạn có thể chia hoặc nhóm một lớp thành nhiều trách nhiệm như bạn muốn. Tuy nhiên, như một nguyên tắc thực tế, SRP sẽ giúp bạn tìm thấy mã bạn cần phải sửa đổi . Dấu hiệu bạn đang vi phạm SRP là:

  • Các lớp học rất lớn, bạn lãng phí thời gian để cuộn hoặc tìm kiếm phương pháp phù hợp.
  • Các lớp học rất nhỏ và nhiều bạn lãng phí thời gian để nhảy giữa chúng hoặc tìm đúng.
  • Khi bạn phải thực hiện một thay đổi, nó ảnh hưởng đến rất nhiều lớp, thật khó để theo dõi.
  • Khi bạn phải thực hiện một thay đổi, không rõ những lớp nào cần thay đổi.

Bạn có thể sửa lỗi này thông qua tái cấu trúc bằng cách cải thiện tên, nhóm mã tương tự lại với nhau, loại bỏ trùng lặp, sử dụng thiết kế lớp và tách / kết hợp các lớp khi cần. Cách tốt nhất để học SRP là lao vào một cơ sở mã hóa và tái cấu trúc nỗi đau.


bạn có thể kiểm tra ví dụ tôi đính kèm bài viết và giải thích câu trả lời của bạn dựa trên nó không.
Songo

Cập nhật. SRP phụ thuộc vào ngữ cảnh, nếu bạn đăng toàn bộ một lớp (trong một câu hỏi riêng) thì sẽ dễ giải thích hơn.
Hội trường Garrett

Cảm ơn các cập nhật. Một câu hỏi mặc dù, đó có thực sự là trách nhiệm của một báo cáo để tự in?! Tại sao bạn không tạo một lớp máy in riêng biệt để báo cáo trong hàm tạo của nó?
Songo

Tôi chỉ nói SRP phụ thuộc vào chính mã mà bạn không nên áp dụng nó một cách giáo điều.
Hội trường Garrett

vâng tôi nhận được quan điểm của bạn. Nhưng nếu bạn đang xây dựng hệ thống từ đầu, bạn sẽ chọn phương án nào? Một Printerlớp lấy một báo cáo hoặc một Reportlớp lấy một máy in? Nhiều lần tôi phải đối mặt với một câu hỏi thiết kế như vậy trước khi tìm hiểu xem liệu mã có chứng minh được sự phức tạp hay không.
Songo

0

Nguyên tắc trách nhiệm duy nhất được kết hợp chặt chẽ với khái niệm về sự gắn kết . Để có một lớp có tính gắn kết cao, bạn cần có sự phụ thuộc đồng thời giữa các biến thể hiện của lớp và các phương thức của nó; nghĩa là, mỗi phương thức nên thao tác càng nhiều biến thể hiện càng tốt. Các phương thức sử dụng càng nhiều biến, càng gắn kết với lớp của nó; sự gắn kết tối đa thường không thể thực hiện được.

Ngoài ra, để áp dụng SRP tốt, bạn hiểu rõ về miền logic nghiệp vụ; để biết mỗi trừu tượng nên làm gì. Kiến trúc phân lớp cũng liên quan đến SRP, bằng cách mỗi lớp thực hiện một việc cụ thể (Lớp nguồn dữ liệu sẽ cung cấp dữ liệu, v.v.).

Quay trở lại sự gắn kết ngay cả khi các phương thức của bạn không sử dụng tất cả các biến, chúng nên được ghép nối:

public class MyClass {
    private Type1 var1;
    private Type2 var2;
    private Type3 var3;

    public Type3 method1() {
        //use var1 and var3
    }  

    public void method2() {
        //use var1 and var2
    }

    public Type1 method3() {
        //use var2 and var3
    }
}

Bạn không nên có một cái gì đó giống như mã dưới đây, trong đó một phần của các biến thể hiện được sử dụng trong một phần của các phương thức và phần khác của các biến được sử dụng trong phần khác của phương thức (ở đây bạn nên có hai lớp cho từng phần của các biến).

public class MyClass {
    private Type1 var1;
    private Type2 var2;
    private Type3 var3;
    private TypeA varA;
    private TypeB varB;

    public Type3 method1() {
        //use var1 and var3
    }  

    public void method2() {
        //use var1 and var2
    }

    public TypeA methodA() {
        //use varA and varB
    }

    public TypeA methodB() {
        //use varA
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.