Trong các ngôn ngữ hướng đối tượng, khi nào các đối tượng nên tự thực hiện các thao tác và khi nào các hoạt động nên được thực hiện trên các đối tượng?


11

Giả sử có một Pagelớp, đại diện cho một bộ hướng dẫn cho trình kết xuất trang. Và giả sử có một Rendererlớp biết cách kết xuất một trang trên màn hình. Có thể cấu trúc mã theo hai cách khác nhau:

/*
 * 1) Page Uses Renderer internally,
 * or receives it explicitly
 */
$page->renderMe(); 
$page->renderMe($renderer); 

/*
 * 2) Page is passed to Renderer
 */
$renderer->renderPage($page);

Những ưu và nhược điểm của từng phương pháp là gì? Khi nào một người sẽ tốt hơn? Khi nào thì người kia sẽ tốt hơn?


LÝ LỊCH

Để thêm một chút nền tảng - tôi thấy mình sử dụng cả hai cách tiếp cận trong cùng một mã. Tôi đang sử dụng thư viện PDF của bên thứ 3 được gọi TCPDF. Ở đâu đó trong mã của tôi, tôi phải có các phần sau để kết xuất PDF hoạt động:

$pdf = new TCPDF();
$html = "some text";
$pdf->writeHTML($html);

Nói rằng tôi muốn tạo một đại diện của trang. Tôi có thể tạo một mẫu chứa các hướng dẫn để hiển thị đoạn trích trang PDF như sau:

/*
 * A representation of the PDF page snippet:
 * a template directing how to render a specific PDF page snippet
 */
class PageSnippet
{    
    function runTemplate(TCPDF $pdf, array $data = null): void
    {
        $pdf->writeHTML($data['html']);
    }
}

/* To be used like so */
$pdf = new TCPDF();
$data['html'] = "some text";
$snippet = new PageSnippet();
$snippet->runTemplate($pdf, $data);

1) Lưu ý ở đây là $snippet tự chạy , như trong ví dụ mã đầu tiên của tôi. Nó cũng cần phải biết và làm quen với $pdf, và với bất kỳ $datađể nó hoạt động.

Nhưng, tôi có thể tạo một PdfRendererlớp như vậy:

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf)
    {
        $this->pdf = $pdf;
    }

    function runTemplate(PageSnippet $template, array $data = null): void
    {
        $template->runTemplate($this->pdf, $data);
    }
}

và sau đó mã của tôi chuyển sang đây:

$renderer = new PdfRenderer(new TCPDF());
$renderer->runTemplate(new PageSnippet(), array('html' => 'some text'));

2) Ở đây $renderernhận được PageSnippetvà bất kỳ $datayêu cầu cho nó để làm việc. Điều này tương tự với ví dụ mã thứ hai của tôi.

Vì vậy, mặc dù trình kết xuất nhận được đoạn mã trang, bên trong trình kết xuất, đoạn mã vẫn tự chạy . Đó là để nói rằng cả hai cách tiếp cận đang chơi. Tôi không chắc chắn nếu bạn có thể hạn chế sử dụng OO của bạn chỉ một hoặc chỉ một. Cả hai có thể được yêu cầu, ngay cả khi bạn che dấu cái khác.


2
Thật không may, bạn đã lang thang vào thế giới của "cuộc chiến tôn giáo" ở đây, dọc theo dòng về việc nên sử dụng khoảng trắng hay tab, sử dụng kiểu giằng, v.v. Không có "tốt hơn" ở đây, chỉ có ý kiến ​​mạnh mẽ từ cả hai phía. Thực hiện tìm kiếm trên internet về những lợi ích và bất lợi của cả mô hình miền giàu và thiếu máu và hình thành ý kiến ​​của riêng bạn.
David Arno

7
@DavidArno Sử dụng không gian bạn bá đạo! :)
candied_orange

1
Ha, đôi khi tôi thực sự không hiểu trang web này. Những câu hỏi hoàn hảo tốt nhận được câu trả lời tốt sẽ được đóng lại ngay lập tức dựa trên quan điểm. Tuy nhiên, một câu hỏi rõ ràng dựa trên ý kiến ​​như thế này xuất hiện và những nghi phạm thông thường không tìm thấy ở đâu. Ồ, nếu bạn không thể đánh bại họ và tất cả những thứ đó ... :)
David Arno

@Erik Eidt, bạn có thể phục hồi câu trả lời của bạn không vì tôi cảm thấy đó là câu trả lời "tùy chọn" rất tốt.
David Arno

1
Ngoài các nguyên tắc RẮN, bạn có thể xem GRASP , đặc biệt là về phần Chuyên gia. Câu hỏi là có thông tin nào để bạn thực hiện trách nhiệm?
OnesimusUnbound

Câu trả lời:


13

Điều này phụ thuộc hoàn toàn vào những gì bạn nghĩ OO .

Đối với OOP = RẮN, hoạt động phải là một phần của lớp nếu nó là một phần của Trách nhiệm duy nhất của lớp.

Đối với OO = công văn ảo / đa hình, hoạt động nên là một phần của đối tượng nếu nó được gửi đi một cách linh hoạt, tức là nếu nó được gọi thông qua một giao diện.

Đối với OO = đóng gói, thao tác phải là một phần của lớp nếu nó sử dụng trạng thái bên trong mà bạn không muốn phơi bày.

Đối với OO =, tôi thích giao diện lưu loát, câu hỏi đặt ra là biến thể nào đọc tự nhiên hơn.

Đối với OO = mô hình hóa các thực thể trong thế giới thực, thực thể nào trong thế giới thực thực hiện thao tác này?


Tất cả các quan điểm đó thường sai trong cách ly. Nhưng đôi khi một hoặc nhiều trong số các quan điểm này hữu ích khi đi đến quyết định thiết kế.

Ví dụ: sử dụng quan điểm đa hình: Nếu bạn có các chiến lược kết xuất khác nhau (như các định dạng đầu ra khác nhau hoặc các công cụ kết xuất khác nhau), thì $renderer->render($page)rất có ý nghĩa. Nhưng nếu bạn có các loại trang khác nhau sẽ được hiển thị khác nhau, $page->render()có thể tốt hơn. Nếu đầu ra phụ thuộc vào cả loại trang và chiến lược kết xuất, bạn có thể thực hiện gửi gấp đôi thông qua mẫu khách truy cập.

Đừng quên rằng trong nhiều ngôn ngữ, các hàm không phải là phương thức. Một chức năng đơn giản như render($page)thường là một giải pháp hoàn toàn tốt (và tuyệt vời đơn giản).


Er đợi một chút. Tôi vẫn có thể nhận được kết xuất đa hình nếu trang giữ tham chiếu đến trình kết xuất nhưng không biết trình kết xuất nào giữ. Nó chỉ có nghĩa là đa hình là một chút xuống lỗ thỏ. Tôi cũng có thể chọn và chọn những gì để chuyển đến trình kết xuất. Tôi không phải vượt qua toàn bộ trang.
candied_orange

@CandiedOrange Đó là một điểm tốt, nhưng tôi sẽ đặt đối số của bạn theo SRP: đó sẽ là Trách nhiệm vốn của Trang để quyết định cách hiển thị, có thể sử dụng một loại chiến lược kết xuất đa hình nào đó.
amon

Tôi hình dung $renderersẽ quyết định làm thế nào để kết xuất. Khi các $pagecuộc nói chuyện với $renderertất cả những gì nó nói là những gì để kết xuất. Không phải thế nào. Không $pagecó ý tưởng như thế nào. Điều đó khiến tôi gặp rắc rối với SRP?
candied_orange

Tôi thực sự không nghĩ rằng chúng tôi không đồng ý. Tôi đã cố gắng sắp xếp nhận xét đầu tiên của bạn vào khung khái niệm của câu trả lời này, nhưng tôi có thể đã sử dụng những từ vụng về. Một điều bạn đang nhắc nhở tôi về điều mà tôi đã không đề cập đến trong câu trả lời: luồng dữ liệu nói không hỏi cũng là một cách giải quyết tốt.
amon

Hừm. Bạn đúng. Những gì tôi đã nói về sẽ làm theo đừng nói. Bây giờ hãy sửa tôi nếu tôi sai. Chiến lược khác, trong đó trình kết xuất lấy tham chiếu trang, có nghĩa là trình kết xuất sẽ phải quay lại và hỏi trang để biết nội dung, sử dụng các trang getters.
candied_orange

2

Theo Alan Kay , các đối tượng là những sinh vật tự cung tự cấp, "trưởng thành" và có trách nhiệm. Người lớn làm mọi việc, họ không được phẫu thuật. Nghĩa là, giao dịch tài chính chịu trách nhiệm tự lưu , trang chịu trách nhiệm hiển thị chính nó , v.v. Chính xác hơn, đóng gói là điều quan trọng trong OOP. Cụ thể, nó thể hiện thông qua nguyên tắc Tell đừng hỏi nổi tiếng (rằng @CandiedOrange thích đề cập mọi lúc :)) và sự ghê tởm công khai của getters và setters .

Trong thực tế, kết quả là các đối tượng sở hữu tất cả các tài nguyên cần thiết để thực hiện công việc của họ, như cơ sở dữ liệu, cơ sở kết xuất, v.v.

Vì vậy, xem xét ví dụ của bạn, phiên bản OOP của tôi sẽ như sau:

class Page
{
    private $data;
    private $renderer;

    public function __construct(ICanRender $renderer, $data)
    {
        $this->renderer = $renderer;
        $this->data = $data;
    }

    public function render()
    {
        $this->renderer->render($this->data);
    }
}

Trong trường hợp bạn quan tâm, David West nói về các nguyên tắc OOP ban đầu trong cuốn sách của mình, Tư duy đối tượng .


1
Nói một cách thẳng thắn, ai quan tâm ai đó nói gì về việc phát triển phần mềm, 15 năm trước, ngoại trừ do lợi ích lịch sử?
David Arno

1
" Tôi quan tâm những gì một người đàn ông đã phát minh ra khái niệm Hướng đối tượng nói về đối tượng là gì. " Tại sao? Ngoài việc cho bạn sử dụng những lời ngụy biện "kháng cáo lên chính quyền" trong các lập luận của bạn, những suy nghĩ của nhà phát minh thuật ngữ có thể mang lại điều gì cho ứng dụng của thuật ngữ này 15 năm sau?
David Arno

2
@Zapadlo: Bạn không trình bày một lý lẽ tại sao tin nhắn từ Trang đến Trình kết xuất chứ không phải theo cách khác. Họ là cả hai đối tượng, và do đó cả hai người lớn, phải không?
JacquesB

1
" Không thể áp dụng lời ngụy biện cho thẩm quyền ở đây " ... " Vì vậy, tập hợp các khái niệm mà theo ý kiến ​​của bạn đại diện cho OOP, thực sự sai [vì đó là một sự sai lệch của định nghĩa ban đầu] ". Tôi hiểu bạn không biết kháng cáo lên ngụy biện là gì? Clue: bạn đã sử dụng một ở đây. :)
David Arno

1
@David Arno Vì vậy, tất cả các kháng cáo lên thẩm quyền sai? Bạn có thích "Kháng cáo ý kiến ​​của tôi?" Mỗi lần ai đó trích dẫn một Bác Bobism, bạn sẽ phàn nàn về sức hấp dẫn đến quyền Zapadio cung cấp một nguồn cũng được kính trọng Bạn có thể không đồng ý, hoặc trích dẫn mâu thuẫn nguồn khác nhau, nhưng repeatefly phàn nàn rằng ai đó đã cung cấp một trích dẫn là không mang tính xây dựng?..
user949300

2

$page->renderMe();

Ở đây chúng tôi đã pagehoàn toàn chịu trách nhiệm cho việc hiển thị chính nó. Nó có thể đã được cung cấp với một kết xuất thông qua một hàm tạo, hoặc nó có thể có chức năng đó được tích hợp.

Tôi sẽ bỏ qua trường hợp đầu tiên (được cung cấp với một kết xuất thông qua một hàm tạo) ở đây, vì nó khá giống với việc chuyển nó dưới dạng tham số. Thay vào đó tôi sẽ xem xét những ưu và nhược điểm của chức năng được tích hợp.

Các pro là nó cho phép đóng gói ở mức rất cao. Các trang cần tiết lộ không có gì về trạng thái bên trong của nó trực tiếp. Nó chỉ phơi bày nó thông qua một kết xuất của chính nó.

Con lừa là nó phá vỡ nguyên tắc trách nhiệm duy nhất (SRP). Chúng tôi có một lớp chịu trách nhiệm đóng gói trạng thái của một trang và cũng được mã hóa cứng với các quy tắc về cách hiển thị chính nó và do đó có thể là một loạt các trách nhiệm khác vì các đối tượng nên "làm mọi thứ cho chính mình, không phải làm cho người khác làm ".

$page->renderMe($renderer);

Ở đây, chúng tôi vẫn đang yêu cầu một trang để có thể tự hiển thị, nhưng chúng tôi đang cung cấp cho nó một đối tượng trợ giúp có thể thực hiện kết xuất thực tế. Hai kịch bản có thể phát sinh ở đây:

  1. Trang chỉ cần biết các quy tắc kết xuất (phương thức nào sẽ gọi theo thứ tự nào) để tạo kết xuất đó. Đóng gói được bảo toàn, nhưng SRP vẫn bị hỏng do trang vẫn phải giám sát quá trình kết xuất, hoặc
  2. Trang chỉ gọi một phương thức trên đối tượng kết xuất, chuyển các chi tiết của nó vào. Chúng ta đang tiến gần hơn đến việc tôn trọng SRP, nhưng giờ chúng ta đã yếu đi việc đóng gói.

$renderer->renderPage($page);

Ở đây, chúng tôi đã hoàn toàn tôn trọng SRP. Đối tượng trang có trách nhiệm giữ thông tin trên một trang và trình kết xuất có trách nhiệm hiển thị trang đó. Tuy nhiên, hiện tại chúng tôi đã làm suy yếu hoàn toàn việc đóng gói đối tượng trang vì nó cần phải làm cho toàn bộ trạng thái của nó, công khai.

Ngoài ra, chúng tôi đã tạo ra một vấn đề mới: trình kết xuất hiện được liên kết chặt chẽ với lớp trang. Điều gì xảy ra khi chúng ta muốn hiển thị một cái gì đó khác với một trang?

Tốt nhất? Không một ai trong số họ. Họ đều có sai sót của họ.


Không đồng ý rằng V3 tôn trọng SRP. Trình kết xuất có ít nhất 2 lý do để thay đổi: nếu Trang thay đổi hoặc nếu cách bạn kết xuất thì nó thay đổi. Và một phần ba, mà bạn thực hiện, nếu Renderer cần kết xuất các đối tượng khác ngoài Pages. Nếu không, phân tích tốt đẹp.
dùng949300

2

Câu trả lời cho câu hỏi này là không rõ ràng. Đó là $renderer->renderPage($page);thực hiện đúng. Để hiểu làm thế nào chúng ta đi đến kết luận này, chúng ta cần hiểu đóng gói.

Một trang là gì? Nó là một đại diện của một màn hình mà ai đó sẽ tiêu thụ. Rằng "ai đó" có thể là người hoặc bot. Lưu ý rằng đó Pagelà một đại diện, và không phải là chính màn hình. Có một đại diện tồn tại mà không được đại diện? Là một trang một cái gì đó mà không có trình kết xuất? Trả lời là Có, một đại diện có thể tồn tại mà không được đại diện. Để đại diện là một giai đoạn sau.

Trình kết xuất không có trang là gì? Trình kết xuất có thể kết xuất mà không có trang không? Không. Vì vậy, một giao diện Renderer không cần renderPage($page);phương thức.

Có chuyện gì với bạn $page->renderMe($renderer);vậy?

Đó là thực tế renderMe($renderer)sẽ vẫn phải gọi nội bộ $renderer->renderPage($page);. Điều này vi phạm Luật Demeter .

Mỗi đơn vị chỉ nên có kiến ​​thức hạn chế về các đơn vị khác

Các Pagelớp không quan tâm liệu có tồn tại một Renderertrong vũ trụ. Nó chỉ quan tâm đến việc là một đại diện của một trang. Vì vậy, lớp hoặc giao diện Rendererkhông bao giờ nên được đề cập bên trong a Page.


CẬP NHẬT TRẢ LỜI

Nếu tôi trả lời đúng câu hỏi của bạn, PageSnippetlớp chỉ nên quan tâm đến việc là một đoạn trang.

class PageSnippet
{    
    /** string */
    private $html;

    function __construct($data = ['html' => '']): void
    {
        $this->html = $data['html'];
    }

   public function getHtml()
   {
       return $this->html;
   }
}

PdfRenderer quan tâm đến kết xuất.

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf = new TCPDF())
    {
        $this->pdf = $pdf;
    }

    function runTemplate(string $html): void
    {
        $this->pdf->writeHTML($html);
    }
}

Sử dụng của khách hàng

$renderer = new PdfRenderer();
$snippet = new PageSnippet(['html' => '<html />']);
$renderer->runTemplate($snippet->getHtml());

Vài điểm cần xem xét:

  • Thực hành xấu của nó để vượt qua $datanhư là một mảng kết hợp. Nó nên là một thể hiện của một lớp.
  • Thực tế là định dạng trang được chứa bên trong htmlthuộc tính của $datamảng là một chi tiết cụ thể cho miền của bạn và PageSnippetnhận thức được chi tiết này.

Nhưng, nếu, ngoài Trang, bạn có Ảnh, Bài viết và Triptich thì sao? Trong sơ đồ của bạn, một Trình kết xuất sẽ phải biết về tất cả chúng. Đó là rất nhiều rò rỉ. Chỉ là thức ăn cho suy nghĩ.
dùng949300

@ user949300: Chà nếu Trình kết xuất cần có khả năng hiển thị hình ảnh, v.v. thì rõ ràng nó cần biết về chúng.
JacquesB

1
Các mẫu thực hành tốt nhất của Smalltalk của Kent Beck giới thiệu mẫu Phương pháp đảo ngược , trong đó cả hai mẫu này đều được hỗ trợ. Bài viết được liên kết cho thấy một đối tượng hỗ trợ một printOn:aStreamphương thức, nhưng tất cả những gì nó làm là bảo luồng phát ra đối tượng. Điểm tương đồng với câu trả lời của bạn là không có lý do gì bạn không thể có cả một trang có thể được kết xuất thành trình kết xuất và trình kết xuất có thể kết xuất một trang, với một triển khai và lựa chọn giao diện thuận tiện.
Graham Lee

2
Bạn sẽ phải phá vỡ / làm hỏng SRP trong mọi trường hợp, nhưng nếu Renderer cần biết cách kết xuất nhiều thứ khác nhau, thì đó thực sự là "nhiều trách nhiệm", và, nếu có thể, nên tránh.
dùng949300

1
Tôi thích câu trả lời của bạn nhưng tôi rất muốn nghĩ rằng việc Pagekhông biết về $ renderer là không thể. Tôi đã thêm một số mã vào câu hỏi của tôi, xem PageSnippetlớp. Đây thực sự là một trang, nhưng nó không thể tồn tại mà không tạo một số loại tham chiếu đến $pdf, trên thực tế là trình kết xuất PDF của bên thứ 3 trong trường hợp này. .. Tuy nhiên, tôi cho rằng mặc dù tôi có thể tạo một PageSnippetlớp như vậy chỉ chứa một mảng các hướng dẫn văn bản cho PDF và có một số lớp khác diễn giải các hướng dẫn đó. Bằng cách đó tôi có thể tránh tiêm $pdfvào PageSnippet, với chi phí cực kỳ phức tạp
Dennis

1

Lý tưởng nhất là bạn muốn càng ít phụ thuộc giữa các lớp càng tốt, vì nó làm giảm độ phức tạp. Một lớp chỉ nên có sự phụ thuộc vào một lớp khác nếu nó thực sự cần nó.

Trạng thái của bạn Pagechứa "một bộ hướng dẫn cho trình kết xuất trang". Tôi tưởng tượng một cái gì đó như thế này:

renderer.renderLine(x, y, w, h, Color.Black)
renderer.renderText(a, b, Font.Helvetica, Color.Black, "bla bla...")
etc...

Vì vậy, nó sẽ là như vậy $page->renderMe($renderer), vì Trang cần một tham chiếu đến trình kết xuất.

Nhưng các hướng dẫn kết xuất khác cũng có thể được thể hiện dưới dạng cấu trúc dữ liệu thay vì các cuộc gọi trực tiếp, vd.

[
  Line(x, y, w, h, Color.Black), 
  Text(a, b, Font.Helvetica, Color.Black, "bla bla...")
]

Trong trường hợp này, Trình kết xuất thực tế sẽ lấy cấu trúc dữ liệu này từ Trang và xử lý nó bằng cách thực hiện các hướng dẫn kết xuất tương ứng. Với cách tiếp cận như vậy, các phụ thuộc sẽ bị đảo ngược - Trang không cần biết về Trình kết xuất, nhưng Trình kết xuất phải được cung cấp Trang mà sau đó có thể kết xuất. Vì vậy, tùy chọn hai:$renderer->renderPage($page);

Vậy cái nào là tốt nhất? Cách tiếp cận đầu tiên có lẽ là đơn giản nhất để thực hiện, trong khi cách thứ hai linh hoạt và mạnh mẽ hơn nhiều, vì vậy tôi đoán nó phụ thuộc vào yêu cầu của bạn.

Nếu bạn không thể quyết định hoặc bạn nghĩ rằng bạn có thể thay đổi cách tiếp cận trong tương lai, bạn có thể ẩn quyết định đằng sau một lớp không xác định, một hàm:

renderPage($page, $renderer)

Cách tiếp cận duy nhất tôi sẽ không đề xuất là $page->renderMe()vì nó gợi ý một trang chỉ có thể có một trình kết xuất duy nhất. Nhưng nếu bạn có một ScreenRenderervà thêm một PrintRenderer? Cùng một trang có thể được hiển thị bởi cả hai.


Trong ngữ cảnh của EPUB hoặc HTML, khái niệm trang không tồn tại mà không có trình kết xuất.
mouviciel

1
@mouviciel: Tôi không chắc là tôi hiểu ý của bạn. Chắc chắn bạn có thể có một trang HTML mà không hiển thị nó? Ví dụ: các trang xử lý trình thu thập thông tin của Google mà không hiển thị chúng.
JacquesB

2
Có một khái niệm khác về trang từ: kết quả của quá trình phân trang khi một trang HTML được định dạng để in, có thể đó là những gì @mouviciel đã nghĩ đến. Tuy nhiên, trong câu hỏi pagenày rõ ràng là một đầu vào cho trình kết xuất, không phải là đầu ra, với khái niệm đó rõ ràng là không phù hợp.
Doc Brown

1

Phần D của RẮN nói

"Trừu tượng không nên phụ thuộc vào chi tiết. Chi tiết nên phụ thuộc vào trừu tượng."

Vì vậy, giữa Trang và Trình kết xuất, có nhiều khả năng là một sự trừu tượng hóa ổn định, ít có khả năng thay đổi, có thể đại diện cho một giao diện? Ngược lại, "chi tiết" là gì?

Theo kinh nghiệm của tôi, sự trừu tượng thường là Trình kết xuất. Ví dụ, nó có thể là một luồng hoặc XML đơn giản, rất trừu tượng và ổn định. Hoặc một số bố cục khá chuẩn. Trang của bạn có nhiều khả năng là một đối tượng kinh doanh tùy chỉnh, một "chi tiết". Và bạn có các đối tượng kinh doanh khác sẽ được hiển thị, chẳng hạn như "hình ảnh", "báo cáo", "biểu đồ", v.v ... (Có lẽ không phải là "tryptich" như trong nhận xét của tôi)

Nhưng nó rõ ràng phụ thuộc vào thiết kế của bạn. Trang có thể là trừu tượng, ví dụ như tương đương <article>với thẻ HTML với các nhánh con tiêu chuẩn. Và bạn có rất nhiều "trình kết xuất" báo cáo kinh doanh tùy chỉnh khác nhau. Trong trường hợp đó, Trình kết xuất sẽ phụ thuộc vào Trang.


0

Tôi nghĩ rằng hầu hết các lớp có thể được chia thành một trong hai loại:

  • Các lớp chứa dữ liệu (có thể thay đổi hoặc không thay đổi)

Đây là những lớp gần như không có sự phụ thuộc vào bất cứ thứ gì khác. Chúng thường là một phần của miền của bạn. Chúng không chứa logic hoặc chỉ logic có thể được bắt nguồn trực tiếp từ trạng thái của nó. Một lớp nhân viên có thể có một chức năng isAdultvì nó có thể được lấy trực tiếp từ nó birthDatenhưng không phải là một chức năng hasBirthDayvì nó yêu cầu thông tin bên ngoài (ngày hiện tại).

  • Các lớp học cung cấp dịch vụ

Các loại lớp này hoạt động trên các lớp khác có chứa dữ liệu. Chúng thường được cấu hình một lần và không thay đổi (vì vậy chúng luôn thực hiện cùng loại chức năng). Tuy nhiên, các loại lớp này vẫn có thể cung cấp một cá thể người trợ giúp có thời gian tồn tại ngắn để thực hiện các hoạt động phức tạp hơn đòi hỏi phải duy trì một số trạng thái trong một thời gian ngắn (như các lớp Builder).

Ví dụ của bạn

Trong ví dụ của bạn, Pagesẽ là một lớp chứa dữ liệu. Nó nên có các hàm để lấy dữ liệu này và có thể sửa đổi nó nếu lớp được cho là có thể thay đổi. Giữ cho nó câm, để nó có thể được sử dụng mà không có nhiều phụ thuộc.

Dữ liệu, hoặc trong trường hợp này, bạn Pagecó thể được trình bày theo vô số cách. Nó có thể được hiển thị dưới dạng trang web, được ghi vào đĩa, được lưu trữ trong cơ sở dữ liệu, được chuyển đổi thành JSON, bất cứ điều gì. Bạn không muốn thêm các phương thức vào một lớp như vậy cho từng trường hợp này (và tạo ra sự phụ thuộc vào tất cả các loại lớp khác, mặc dù lớp của bạn được cho là chỉ chứa dữ liệu).

Bạn Rendererlà một loại dịch vụ điển hình. Nó có thể hoạt động trên một tập hợp dữ liệu nhất định và trả về kết quả. Nó không có nhiều trạng thái của riêng nó, và trạng thái của nó thường là bất biến, có thể được cấu hình một lần và sau đó được sử dụng lại.

Ví dụ, bạn có thể có một MobileRendererStandardRenderercả hai triển khai Rendererlớp nhưng với các cài đặt khác nhau.

Vì vậy, khi Pagechứa dữ liệu và nên được giữ im lặng, giải pháp sạch nhất trong trường hợp này sẽ là truyền Pagecho Renderer:

$renderer->renderPage($page)

2
Rất logic thủ tục.
user949300
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.