Các lớp tách từ giao diện người dùng


27

Cách thực hành tốt nhất khi viết các lớp có thể phải biết về giao diện người dùng. Không phải một lớp biết cách tự vẽ sẽ phá vỡ một số thực tiễn tốt nhất vì nó phụ thuộc vào giao diện người dùng là gì (giao diện điều khiển, GUI, v.v.)?

Trong nhiều cuốn sách lập trình tôi đã bắt gặp ví dụ "Hình dạng" thể hiện sự kế thừa. Hình dạng lớp cơ sở có một phương thức draw () mà mỗi hình dạng như hình tròn và hình vuông ghi đè. Điều này cho phép đa hình. Nhưng không phải phương thức draw () phụ thuộc rất nhiều vào giao diện người dùng là gì? Nếu chúng ta viết lớp này để nói, Win Forms, thì chúng ta không thể sử dụng lại nó cho một ứng dụng bảng điều khiển hoặc ứng dụng web. Điều này có đúng không?

Lý do cho câu hỏi là tôi thấy mình luôn bị mắc kẹt và gác máy về cách khái quát hóa các lớp học để chúng hữu ích nhất. Điều này thực sự đang chống lại tôi và tôi tự hỏi nếu tôi "cố gắng quá sức".


Tại sao bạn muốn tách rời? Bởi vì bạn nghe nói đó là điều đúng đắn để làm hoặc bạn có lý do khác?
SoylentGray

2
Toàn bộ "lớp biết cách tự vẽ" chỉ là một ví dụ kinh khủng, cổ xưa mà tôi ước sẽ biến mất. Đặc biệt là trên stack của lập trình viên trò chơi =)
Patrick Hughes

2
@Chad Tôi không thực sự có nhiều kinh nghiệm ở ngoài trường. Tôi đã đọc sách và tôi thực sự thích học và đọc những điều mới về các mẫu thiết kế và thực tiễn tốt nhất. Vì vậy, có, bạn có thể nói rằng tôi đã nghe nói rằng tách rời là tốt nhưng nó cũng có ý nghĩa. Tôi muốn viết mã mà tôi có thể sử dụng cho ứng dụng WinForms trên máy tính để bàn, ví dụ, sau đó lấy mã đó và sử dụng lại càng nhiều càng tốt cho một trang web hoặc thậm chí là ứng dụng Silverlight.
Pete

@Pete - Đó là một câu trả lời tốt.
SoylentGray

2
@Patrick: công bằng mà nói, nếu bạn đang viết một Shapelớp, thì có lẽ bạn đang tự viết ngăn xếp đồ họa, chứ không phải viết một ứng dụng khách vào ngăn xếp đồ họa.
Ken Bloom

Câu trả lời:


13

Cách thực hành tốt nhất khi viết các lớp có thể phải biết về giao diện người dùng. Không phải một lớp biết cách tự vẽ sẽ phá vỡ một số thực tiễn tốt nhất vì nó phụ thuộc vào giao diện người dùng là gì (giao diện điều khiển, GUI, v.v.)?

Điều đó phụ thuộc vào lớp học và trường hợp sử dụng. Một yếu tố trực quan biết cách tự vẽ không nhất thiết là vi phạm nguyên tắc trách nhiệm duy nhất.

Trong nhiều cuốn sách lập trình tôi đã bắt gặp ví dụ "Hình dạng" thể hiện sự kế thừa. Hình dạng lớp cơ sở có một phương thức draw () mà mỗi hình dạng như hình tròn và hình vuông ghi đè. Điều này cho phép đa hình. Nhưng không phải phương thức draw () phụ thuộc rất nhiều vào giao diện người dùng là gì?

Một lần nữa, không nhất thiết. Nếu bạn có thể tạo giao diện (drawpoint, drawLine, đặt Color, v.v.), bạn có thể vượt qua bất kỳ bối cảnh nào để vẽ mọi thứ lên hình dạng, ví dụ như trong hàm tạo của hình dạng. Điều này sẽ cho phép các hình dạng tự vẽ trên bảng điều khiển hoặc bất kỳ khung vẽ nào được cung cấp.

Nếu chúng ta viết lớp này để nói, Win Forms, thì chúng ta không thể sử dụng lại nó cho một ứng dụng bảng điều khiển hoặc ứng dụng web. Điều này có đúng không?

Vâng, đó là sự thật. Nếu bạn viết UserControl (không phải là một lớp nói chung) cho Windows Forms, thì bạn sẽ không thể sử dụng nó với bảng điều khiển. Nhưng đó không phải là vấn đề. Tại sao bạn lại mong đợi UserControl cho Windows Forms hoạt động với bất kỳ loại bản trình bày nào? UserControl nên làm một việc và làm tốt. Nó bị ràng buộc với một hình thức trình bày nhất định theo định nghĩa. Cuối cùng, người dùng cần một cái gì đó cụ thể và không trừu tượng. Điều này có thể chỉ đúng một phần đối với các khung, nhưng đối với các ứng dụng của người dùng cuối thì đúng như vậy.

Tuy nhiên, logic đằng sau nó nên được tách rời, vì vậy bạn có thể sử dụng lại nó với các công nghệ trình bày khác. Giới thiệu các giao diện khi cần thiết, để duy trì tính trực giao cho ứng dụng của bạn. Nguyên tắc chung là: Những thứ cụ thể nên được trao đổi với những thứ cụ thể khác.

Lý do cho câu hỏi là tôi thấy mình luôn bị mắc kẹt và gác máy về cách khái quát hóa các lớp học để chúng hữu ích nhất. Điều này thực sự đang chống lại tôi và tôi tự hỏi nếu tôi "cố gắng quá sức".

Bạn biết đấy, các lập trình viên cực đoan rất thích thái độ YAGNI của họ . Đừng cố viết mọi thứ một cách khái quát và đừng quá cố gắng để làm mọi thứ có mục đích chung. Điều này được gọi là quá mức và cuối cùng sẽ dẫn đến mã hoàn toàn phức tạp. Cung cấp cho mỗi thành phần chính xác một nhiệm vụ và đảm bảo nó thực hiện tốt. Đặt các tóm tắt khi cần thiết, nơi bạn mong muốn mọi thứ thay đổi (ví dụ: giao diện để vẽ bối cảnh, như đã nêu ở trên).

Nói chung, khi viết các ứng dụng kinh doanh, bạn nên luôn cố gắng tách rời mọi thứ. MVC và MVVM là tuyệt vời để tách rời logic khỏi bản trình bày, vì vậy bạn có thể sử dụng lại nó cho bản trình bày web hoặc ứng dụng bảng điều khiển. Hãy nhớ rằng cuối cùng, một số điều phải cụ thể. Người dùng của bạn không thể làm việc với sự trừu tượng, họ cần một cái gì đó cụ thể. Trừu tượng chỉ là người trợ giúp cho bạn, lập trình viên, để giữ cho mã có thể mở rộng và duy trì được. Bạn cần phải biết về nơi bạn cần mã của mình để linh hoạt. Cuối cùng, tất cả sự trừu tượng phải sinh ra một cái gì đó cụ thể.

Chỉnh sửa: Nếu bạn muốn đọc thêm về kiến ​​trúc và kỹ thuật thiết kế có thể cung cấp các thực tiễn tốt nhất, tôi khuyên bạn nên đọc câu trả lời @Catchops và đọc về thực tiễn RẮN trên wikipedia.

Ngoài ra, đối với người mới bắt đầu, tôi luôn giới thiệu cuốn sách sau: Head First Design Forms . Nó sẽ giúp bạn hiểu các kỹ thuật trừu tượng / thực hành thiết kế OOP, hơn cả cuốn sách GoF (rất hay, nó không phù hợp với người mới bắt đầu).


1
Câu trả lời hay, nhưng các lập trình viên cực đoan không 'đưa vào sự trừu tượng nơi chúng ta mong đợi mọi thứ sẽ thay đổi'. Chúng tôi đưa vào sự trừu tượng nơi mọi thứ đang thay đổi, để DRY lên mã.
kevin cline

1
@kevin cline thật tuyệt vời trừ khi bạn đang thiết kế một thư viện có sẵn công khai với API cần tuân thủ các hành vi và giao diện trước đó.
Magnus Wolffelt

@Magnus - vâng, thiết kế một thư viện bằng một số ngôn ngữ, lập kế hoạch cho khả năng tương thích nhị phân lạc hậu, là khó khăn. Người ta buộc phải viết tất cả các loại mã hiện không cần thiết để cho phép mở rộng trong tương lai. Điều này có thể hợp lý cho các ngôn ngữ biên dịch với kim loại. Thật ngớ ngẩn cho các ngôn ngữ biên dịch thành máy ảo.
kevin cline

19

Bạn chắc chắn đúng. Và không, bạn đang "cố gắng tốt" :)

Đọc về nguyên tắc trách nhiệm duy nhất

Hoạt động bên trong của bạn về lớp và cách trình bày thông tin này cho người dùng là hai trách nhiệm.

Đừng sợ để tách lớp. Hiếm khi vấn đề là quá nhiều trừu tượng và tách rời :)

Hai mẫu rất có liên quan là Bộ điều khiển chế độ xem mô hình mô-đun cho các ứng dụng web và Chế độ xem mô hình ViewModel cho Silverlight / WPF.


1
+1 Bạn thậm chí có thể sử dụng MVC cho các ứng dụng không phải web, ít nhất nó cũng giúp bạn suy nghĩ về các điều khoản giúp giữ rõ trách nhiệm.
Patrick Hughes

MVVM là silimar cho MVC. MVC chủ yếu dành cho các ứng dụng phi trạng thái như các ứng dụng web.
Boris Yankov

3
-1 Theo kinh nghiệm của tôi, một trong những vấn đề phổ biến nhất là khái quát hóa sớm và kỹ thuật quá mức nói chung.
Dietbuddha

3
Trước tiên bạn cần biết cách thiết kế một cái gì đó để thiết kế nó quá mức. Theo kinh nghiệm của tôi, phần mềm 'kỹ sư dưới quyền' của mọi người thường xuyên hơn nhiều.
Boris Yankov

Mặc dù tôi đánh giá cao những nỗ lực cải tiến thiết kế phần mềm, thay thế một cuộc gọi trên giao diện, cho một cuộc gọi trên một lớp học, không tách rời bất cứ điều gì về mặt ngữ nghĩa. Xem xét những gì xảy ra khi sử dụng một ngôn ngữ gõ động. Có quá nhiều sự nhấn mạnh vào việc tách rời (cú pháp) hời hợt và rất ít về sự tách rời thực sự, trong lời khuyên được cung cấp trong phần lập trình viên của trao đổi ngăn xếp.
Frank Hileman

7

Tôi sử dụng MVVM rất nhiều và theo ý kiến ​​của tôi, một lớp đối tượng kinh doanh không bao giờ cần phải biết bất cứ điều gì về giao diện người dùng. Chắc chắn rằng họ có thể cần biết SelectedItem, hoặc IsChecked, hoặc IsVisible, v.v. nhưng những giá trị đó không cần phải liên kết với bất kỳ UI cụ thể nào và có thể là các thuộc tính chung trên lớp.

Nếu bạn cần làm gì đó với giao diện mã phía sau, chẳng hạn như đặt Focus, chạy Animation, xử lý Phím nóng, v.v. thì mã phải là một phần của mã phía sau của UI, chứ không phải các lớp logic nghiệp vụ của bạn.

Vì vậy, tôi sẽ nói đừng ngừng cố gắng tách giao diện người dùng và các lớp học của bạn. Chúng càng tách rời, chúng càng dễ bảo trì và kiểm tra.


5

Có một số mẫu thiết kế đã được thử và đúng đã được phát triển qua nhiều năm để giải quyết chính xác những gì bạn đang nói. Các câu trả lời khác cho câu hỏi của bạn đã đề cập đến Nguyên tắc lặp lại duy nhất - hoàn toàn hợp lệ - và điều gì dường như đang thúc đẩy câu hỏi của bạn. Nguyên tắc đó chỉ đơn giản nói rằng một lớp cần phải làm một điều gì đó. Nói cách khác, nâng cao sự gắn kết và hạ thấp Khớp nối, đó là điều mà thiết kế hướng đối tượng tốt là tất cả - một lớp có làm tốt một việc không và không có nhiều sự phụ thuộc vào người khác.

Chà ... bạn đã đúng khi quan sát rằng nếu bạn muốn vẽ một vòng tròn trên iPhone, nó sẽ khác với vẽ một hình trên PC chạy windows. Bạn PHẢI có (trong trường hợp này) một lớp cụ thể vẽ một cái tốt trên iPhone và một cái khác vẽ một cái tốt trên PC. Đây là nơi mà quyền thuê nhà OO cơ bản mà tất cả các ví dụ về hình dạng đó bị phá vỡ. Bạn chỉ đơn giản là không thể làm điều đó với thừa kế một mình.

Đó là nơi các giao diện xuất hiện - như tiểu bang của cuốn sách Gang of Four (PHẢI ĐỌC) - Luôn ưu tiên thực hiện hơn kế thừa. Nói cách khác, sử dụng các giao diện để ghép một kiến ​​trúc có thể thực hiện các chức năng khác nhau theo nhiều cách mà không phụ thuộc vào các phụ thuộc được mã hóa cứng.

Tôi đã thấy tham chiếu đến các nguyên tắc RẮN . Đó là những điều tuyệt vời. 'S' là nguyên tắc phản hồi duy nhất. NHƯNG, 'D' là viết tắt của Sự phụ thuộc đảo ngược. Mẫu Inversion of Control (Dependency Injection) có thể được sử dụng ở đây. Nó rất mạnh mẽ và có thể được sử dụng để trả lời câu hỏi làm thế nào để kiến ​​trúc sư một hệ thống có thể vẽ một vòng tròn cho iPhone cũng như một cho PC.

Có thể tạo một kiến ​​trúc có chứa các quy tắc kinh doanh và truy cập dữ liệu phổ biến, nhưng có nhiều cách triển khai giao diện người dùng bằng các cấu trúc này. Tuy nhiên, điều đó thực sự có ích khi thực sự tham gia vào một nhóm thực hiện nó và xem nó hoạt động để thực sự hiểu về nó.

Đây chỉ là một câu trả lời cấp cao nhanh chóng cho một câu hỏi xứng đáng có câu trả lời chi tiết hơn. Tôi khuyến khích bạn nhìn xa hơn vào các mẫu này. Một số triển khai cụ thể hơn của các patters này có thể được tìm thấy như các tên nổi tiếng của MVC và MVVM.

Chúc may mắn!


Tôi thích nó khi ai đó hạ thấp một cái gì đó mà không đưa ra nhận xét về lý do tại sao (tất nhiên là mỉa mai). Các nguyên tắc tôi đã nêu là đúng - xin các downvoter vui lòng đứng lên và nói lý do tại sao.
Bắt

2

Cách thực hành tốt nhất khi viết các lớp có thể phải biết về giao diện người dùng.

Không phải một lớp biết cách tự vẽ sẽ phá vỡ một số thực tiễn tốt nhất vì nó phụ thuộc vào giao diện người dùng là gì (giao diện điều khiển, GUI, v.v.)?

Trong trường hợp này, bạn vẫn có thể sử dụng MVC / MVVM và thực hiện các triển khai UI khác nhau bằng giao diện chung:

public interface IAgnosticChartDrawing
{
   public void Draw(ChartProperties chartProperties);
   event EventHandler ChartPanned;
}

public class GuiChartDrawer : UserControl, IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //GDI, GTK or something else...
    }

    //Implement event based on mouse actions
}

public class ConsoleChartDrawer : IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //'Draw' using characters and symbols...
    }

    //Implement event based on keyboard actions
}

IAgnosticChartDrawing guiView = new GuiChartDrawer();
IAgnosticChartDrawing conView = new ConsoleChartDrawer();

Model model = new FinancialModel();

SampleController controllerGUI = new SampleController(model, guiView);
SampleController controllerConsole = new SampleController(model, conView);

Bằng cách này, bạn sẽ có thể sử dụng lại Bộ điều khiển và Mô hình logic trong khi có thể thêm các loại GUI mới.


2

Có nhiều mẫu khác nhau để làm điều đó: MVP, MVC, MVVM, v.v ...

Một bài viết hay từ Martin Fowler (tên lớn) để đọc là GUI Architectures: http://www.martinfowler.com/eaaDev/uiArchs.html

MVP chưa được đề cập nhưng nó chắc chắn đáng được trích dẫn: hãy xem nó.

Đó là mẫu được đề xuất bởi các nhà phát triển Bộ công cụ web của Google để sử dụng, nó thực sự gọn gàng.

Bạn có thể tìm thấy mã thực, ví dụ thực và lý do tại sao cách tiếp cận này hữu ích ở đây:

http://code.google.com.vn/webtoolkit/articles/mvp-arch architecture.html

http://code.google.com.vn/webtoolkit/articles/mvp-arch architecture-2.html

Một trong những lợi thế của việc làm theo phương pháp này hoặc tương tự chưa được nhấn mạnh đủ ở đây là khả năng kiểm tra! Trong rất nhiều trường hợp tôi sẽ nói đó là lợi thế chính!


1
+1 cho các liên kết. Tôi đã đọc một bài viết tương tự trên MSDN về MVVM và những bài viết trên google tốt hơn nhiều mặc dù trên một mô hình hơi khác.
Pete

1

Đây là một trong những nơi mà OOP không thực hiện tốt công việc trừu tượng. Đa hình OOP sử dụng công văn động trên một biến duy nhất ('this'). Nếu tính đa hình được bắt nguồn từ Shape thì bạn không thể gửi đồng minh đa hình trên trình kết xuất (console, GUI, v.v.).

Hãy xem xét một hệ thống lập trình có thể gửi hai hoặc nhiều biến:

poly_draw(Shape s, Renderer r)

và cũng giả sử hệ thống có thể cung cấp cho bạn cách thể hiện poly_draw cho các kết hợp khác nhau của các loại Hình dạng và loại Trình kết xuất. Sau đó, thật dễ dàng để đưa ra một phân loại hình dạng và kết xuất phải không? Trình kiểm tra loại bằng cách nào đó sẽ giúp bạn tìm ra là có các kết hợp hình dạng và kết xuất mà bạn có thể đã bỏ lỡ khi thực hiện.

Hầu hết các ngôn ngữ OOP không hỗ trợ bất cứ điều gì như trên (một số ít làm, nhưng chúng không phải là chính thống). Tôi sẽ đề nghị bạn hãy xem mẫu Khách truy cập để biết cách giải quyết.


1

... không phải phương thức draw () phụ thuộc rất nhiều vào giao diện người dùng là gì? Nếu chúng ta viết lớp này để nói, Win Forms, thì chúng ta không thể sử dụng lại nó cho một ứng dụng bảng điều khiển hoặc ứng dụng web. Điều này có đúng không?

Trên đây âm thanh chính xác với tôi. Theo hiểu biết của tôi, người ta có thể nói nó có nghĩa là sự kết hợp tương đối chặt chẽ giữa Bộ điều khiểnChế độ xem theo mẫu thiết kế MVC. Điều này cũng có nghĩa là để chuyển đổi giữa máy tính để bàn-bàn điều khiển-webapp, người ta sẽ phải chuyển đổi cả Bộ điều khiển và Chế độ xem thành một cặp - chỉ mô hình vẫn không thay đổi.

... Tôi thấy mình luôn bị mắc kẹt và gác máy về cách khái quát hóa các lớp học để chúng hữu ích nhất.

Vâng, hiện tại của tôi ở trên là khớp nối View-Controller này mà chúng ta đang nói đến là OK và thậm chí hơn nữa, nó khá hợp thời trong thiết kế hiện đại.

Mặc dù, một hoặc hai năm trước tôi cũng cảm thấy không an toàn về điều đó. Tôi đã thay đổi suy nghĩ của mình sau khi nghiên cứu các cuộc thảo luận tại diễn đàn Sun về các mẫu và thiết kế OO.

Nếu bạn quan tâm, hãy tự mình thử diễn đàn này - nó đã được chuyển sang Oracle ngay bây giờ ( liên kết ). Nếu bạn đến đó, hãy thử ném anh chàng Saish - hồi đó, những lời giải thích của anh ấy về những vấn đề khó khăn này hóa ra lại hữu ích nhất với tôi. Tôi không thể biết mặc dù anh ấy vẫn tham gia - bản thân tôi đã không ở đó khá lâu


1

Nhưng không phải phương thức draw () phụ thuộc rất nhiều vào giao diện người dùng là gì?

Từ một kiểu xem thực dụng, một số mã trong hệ thống của bạn cần biết cách vẽ một cái gì đó giống như Rectanglenếu đó là yêu cầu cuối của người dùng. Và điều đó sẽ sôi sục vào một lúc nào đó để làm những việc thực sự ở mức độ thấp như raster pixel hoặc hiển thị một cái gì đó trong bảng điều khiển.

Câu hỏi cho tôi từ quan điểm khớp nối là ai / cái gì nên phụ thuộc vào loại thông tin này, và mức độ chi tiết nào (như thế nào trừu tượng, ví dụ)?

Tóm tắt khả năng vẽ / kết xuất

Bởi vì nếu mã bản vẽ cấp cao hơn chỉ phụ thuộc vào thứ gì đó rất trừu tượng, thì sự trừu tượng đó có thể có thể hoạt động (thông qua việc thay thế các triển khai cụ thể) trên tất cả các nền tảng bạn định nhắm mục tiêu. Như một ví dụ giả định, một số IDrawergiao diện rất trừu tượng có thể có khả năng được triển khai trong cả API giao diện điều khiển và GUI để làm những việc như hình dạng cốt truyện (việc triển khai giao diện điều khiển có thể coi giao diện điều khiển như một "hình ảnh" 80xN với nghệ thuật ASCII). Tất nhiên đó là một ví dụ giả định vì đó thường không phải là điều bạn muốn làm là coi đầu ra của giao diện điều khiển như bộ đệm hình ảnh / khung; thông thường hầu hết các nhu cầu cuối của người dùng đều yêu cầu nhiều tương tác dựa trên văn bản hơn trong bảng điều khiển.

Một xem xét khác là làm thế nào dễ dàng để thiết kế một trừu tượng ổn định? Bởi vì có thể dễ dàng nếu tất cả những gì bạn nhắm đến là các API GUI hiện đại để trừu tượng hóa các khả năng vẽ hình cơ bản như vẽ đường thẳng, hình chữ nhật, đường dẫn, văn bản, những thứ thuộc loại này (chỉ đơn giản là rasterization 2D một bộ nguyên thủy hạn chế) , với một giao diện trừu tượng có thể dễ dàng thực hiện cho tất cả chúng thông qua các kiểu con khác nhau với ít chi phí. Nếu bạn có thể thiết kế một cách trừu tượng như vậy một cách hiệu quả và thực hiện nó trên tất cả các nền tảng đích, thì tôi sẽ nói rằng đó là một điều xấu xa hơn nhiều, thậm chí là một điều ác, đối với một hình dạng hoặc điều khiển GUI hoặc bất cứ điều gì biết cách tự vẽ bằng cách sử dụng một trừu tượng hóa.

Nhưng giả sử bạn đang cố gắng trừu tượng hóa các chi tiết khác nhau giữa Playstation Portable, iPhone, XBox One và PC chơi game mạnh mẽ trong khi nhu cầu của bạn là sử dụng các kỹ thuật kết xuất / tạo bóng 3D thời gian thực tiên tiến nhất trên mỗi thiết bị . Trong trường hợp đó, cố gắng đưa ra một giao diện trừu tượng để trừu tượng hóa các chi tiết kết xuất khi các khả năng và API phần cứng cơ bản khác nhau đến mức gần như chắc chắn sẽ dẫn đến việc thiết kế và thiết kế lại rất nhiều thời gian, xác suất cao của thiết kế định kỳ thay đổi với không lường trước được những khám phá và tương tự là một giải pháp mẫu số chung thấp nhất không khai thác được toàn bộ tính duy nhất và sức mạnh của phần cứng cơ bản.

Làm cho dòng chảy phụ thuộc theo hướng ổn định, thiết kế "dễ dàng"

Trong lĩnh vực của tôi, tôi đang ở trong kịch bản cuối cùng đó. Chúng tôi nhắm mục tiêu rất nhiều phần cứng khác nhau với các khả năng và API cơ bản hoàn toàn khác nhau và cố gắng đưa ra một bản tóm tắt vẽ / vẽ để thống trị tất cả là vô vọng (chúng tôi có thể trở nên nổi tiếng thế giới khi làm điều đó một cách hiệu quả vì nó sẽ là một trò chơi người thay đổi trong ngành). Vì vậy, điều cuối cùng tôi muốn trong trường hợp của tôi là như analogical Shapehay Modelhay Particle Emittermà biết làm thế nào để vẽ bản thân, ngay cả khi nó được bày tỏ rằng bản vẽ trong cấp cao nhất và cách mà hầu hết trừu tượng có thể ...

... bởi vì những trừu tượng đó quá khó để thiết kế chính xác và khi một thiết kế khó có thể chính xác, và mọi thứ phụ thuộc vào nó, đó là một công thức cho những thay đổi thiết kế trung tâm tốn kém nhất mà gợn và phá vỡ mọi thứ tùy thuộc vào nó. Vì vậy, điều cuối cùng bạn muốn là cho các phụ thuộc trong hệ thống của bạn chuyển sang các thiết kế trừu tượng quá khó để có được chính xác (quá khó để ổn định mà không có thay đổi xâm nhập).

Khó phụ thuộc vào dễ, không dễ phụ thuộc vào khó

Vì vậy, những gì chúng tôi làm thay vì làm cho các phụ thuộc chảy vào những thứ dễ thiết kế. Thật dễ dàng hơn nhiều để thiết kế một "Mô hình" trừu tượng, chỉ tập trung vào việc lưu trữ những thứ như đa giác và vật liệu và làm cho thiết kế đó chính xác hơn là thiết kế một "Trình kết xuất" trừu tượng có thể được thực hiện một cách hiệu quả (thông qua các kiểu con bê tông thay thế) cho bản vẽ dịch vụ yêu cầu thống nhất cho phần cứng khác nhau như PSP từ PC.

nhập mô tả hình ảnh ở đây

Vì vậy, chúng tôi đảo ngược sự phụ thuộc ra khỏi những thứ khó thiết kế. Thay vì làm cho các mô hình trừu tượng biết cách tự vẽ một thiết kế kết xuất trừu tượng mà tất cả chúng phụ thuộc vào (và phá vỡ các triển khai của chúng nếu thiết kế đó thay đổi), thay vào đó chúng ta có một trình kết xuất trừu tượng biết cách vẽ mọi đối tượng trừu tượng trong cảnh của chúng ta ( các mô hình, trình phát hạt, v.v.) và do đó chúng ta có thể triển khai một kiểu con trình kết xuất OpenGL cho PC như RendererGl, một kiểu khác cho PSP như RendererPsp, một kiểu khác cho điện thoại di động, v.v. Trong trường hợp đó, các phụ thuộc đang chuyển sang các thiết kế ổn định, dễ dàng để có được chính xác, từ trình kết xuất đến các loại thực thể khác nhau (mô hình, hạt, kết cấu, v.v.) trong cảnh của chúng tôi, không phải theo cách khác.

nhập mô tả hình ảnh ở đây

  • Tôi đang sử dụng "tính ổn định / không ổn định" theo nghĩa hơi khác so với chỉ số khớp nối liên kết / hiệu quả của chú Bob đang đo lường nhiều khó khăn của sự thay đổi theo như tôi có thể hiểu. Tôi đang nói nhiều hơn về "xác suất yêu cầu thay đổi", mặc dù chỉ số ổn định của anh ta rất hữu ích ở đó. Khi "xác suất thay đổi" tỷ lệ thuận với "dễ thay đổi" (ví dụ: những thứ có khả năng yêu cầu thay đổi có độ khớp cao nhất và không ổn định từ số liệu của chú Bob), thì mọi thay đổi có thể xảy ra như vậy đều rẻ và không xâm phạm , chỉ yêu cầu thay thế một triển khai mà không chạm vào bất kỳ thiết kế trung tâm.

Nếu bạn thấy mình đang cố gắng trừu tượng hóa một thứ gì đó ở cấp độ trung tâm của cơ sở mã của mình và nó quá khó để thiết kế, thay vì đập đầu một cách bướng bỉnh vào tường và liên tục thực hiện các thay đổi xâm phạm mỗi tháng / năm yêu cầu cập nhật 8.000 tệp nguồn vì nó phá vỡ mọi thứ phụ thuộc vào nó, đề nghị số một của tôi là xem xét đảo ngược các phụ thuộc. Xem bạn có thể viết mã theo cách khó thiết kế hay không phụ thuộc vào mọi thứ khác dễ thiết kế hơn, không có những thứ dễ thiết kế hơn tùy thuộc vào thứ rất khó thiết kế. Lưu ý rằng tôi đang nói về thiết kế (cụ thể là thiết kế giao diện) chứ không phải triển khai: đôi khi mọi thứ dễ thiết kế và khó thực hiện, và đôi khi mọi thứ khó thiết kế nhưng dễ thực hiện. Sự phụ thuộc chảy vào các thiết kế, vì vậy, trọng tâm chỉ nên tập trung vào mức độ khó của việc thiết kế ở đây để xác định hướng mà phụ thuộc chảy.

Nguyên tắc trách nhiệm duy nhất

Đối với tôi SRP không quá thú vị ở đây thường (mặc dù tùy thuộc vào ngữ cảnh). Ý tôi là có một hành động cân bằng chặt chẽ trong việc thiết kế những thứ rõ ràng có mục đích và có thể bảo trì được, nhưng Shapeđối tượng của bạn có thể phải tiết lộ thông tin chi tiết hơn nếu họ không biết cách tự vẽ, chẳng hạn, và có thể không có nhiều điều có ý nghĩa làm với một hình dạng trong một bối cảnh sử dụng cụ thể hơn là xây dựng nó và vẽ nó. Có sự đánh đổi với tất cả mọi thứ, và nó không liên quan đến SRP có thể khiến mọi thứ nhận thức được làm thế nào để bản thân có khả năng trở thành cơn ác mộng duy trì như vậy trong trải nghiệm của tôi trong những bối cảnh nhất định.

Nó có nhiều hơn để làm với khớp nối và hướng mà phụ thuộc chảy trong hệ thống của bạn. Nếu bạn đang cố gắng chuyển giao diện kết xuất trừu tượng mà mọi thứ phụ thuộc vào (vì họ đang sử dụng nó để tự vẽ) sang API / phần cứng mục tiêu mới và nhận ra rằng bạn phải thay đổi đáng kể thiết kế của nó để làm cho nó hoạt động hiệu quả ở đó, sau đó đó là một thay đổi rất tốn kém để thực hiện đòi hỏi phải thay thế việc triển khai mọi thứ trong hệ thống của bạn để biết cách tự vẽ. Và đó là vấn đề bảo trì thiết thực nhất mà tôi gặp phải với những điều nhận thức về cách tự vẽ nếu điều đó chuyển thành một khối lượng phụ thuộc chảy vào trừu tượng, quá khó để thiết kế chính xác.

Niềm tự hào của nhà phát triển

Tôi đề cập đến một điểm này bởi vì, theo kinh nghiệm của tôi, đây thường là trở ngại lớn nhất để chuyển hướng phụ thuộc vào những thứ dễ thiết kế hơn. Rất dễ dàng để các nhà phát triển có một chút tham vọng ở đây và nói: "Tôi sẽ thiết kế sự trừu tượng hóa kết xuất đa nền tảng để cai trị tất cả, tôi sẽ giải quyết những gì các nhà phát triển khác dành hàng tháng để chuyển và tôi sẽ nhận được đúng và nó sẽ hoạt động như ma thuật trên mọi nền tảng mà chúng tôi hỗ trợ và sử dụng các kỹ thuật kết xuất hiện đại trên mọi nền tảng; tôi đã hình dung ra nó trong đầu. "Trong trường hợp họ chống lại giải pháp thực tế đó là tránh làm điều đó và chỉ cần lật hướng phụ thuộc và dịch những gì có thể rất tốn kém và thay đổi thiết kế trung tâm thành những thay đổi định kỳ đơn giản và rẻ tiền để thực hiện. Cần phải có một bản năng "cờ trắng" nào đó trong các nhà phát triển để từ bỏ khi một thứ gì đó quá khó để thiết kế đến mức trừu tượng như vậy và xem xét lại toàn bộ chiến lược của họ, nếu không họ sẽ rất đau buồn và đau đớn. Tôi sẽ đề nghị chuyển những tham vọng và tinh thần chiến đấu như vậy sang các triển khai hiện đại của một thứ dễ dàng hơn để thiết kế hơn là đưa tham vọng chinh phục thế giới này đến mức thiết kế giao diện.


1
Tôi dường như không thể nắm bắt được ý tưởng "đảo ngược sự phụ thuộc ra khỏi những thứ khó thiết kế", bạn chỉ nói về thừa kế? Sử dụng ví dụ về PSP / OpenGL, ý bạn là thay vì tạo một OpenGLBillboard, bạn sẽ tạo ra một OpenGLRenderercách biết để vẽ bất kỳ loại IBillBoardnào? Nhưng nó sẽ làm điều đó bằng cách ủy thác logic IBillBoard, hoặc nó sẽ có các công tắc hoặc IBillBoardloại lớn với các điều kiện? Đó là điều tôi cảm thấy khó nắm bắt, bởi vì điều đó dường như không thể duy trì được!
Steve Chamaillard

@SteveChamaillard Sự khác biệt là, PSP (phần cứng hạn chế) phải xử lý các nguyên hàm khối lượng bằng cách sử dụng độ trong suốt của trường học cũ và một thứ tự đánh giá kết xuất khác nhau: digitalrune.github.io/DigitalRune-Documentation/media/ ,. Khi bạn có một trung tâm RendererPspbiết được sự trừu tượng cấp cao trong bối cảnh trò chơi của mình, thì nó có thể thực hiện tất cả các phép thuật và backflips mà nó cần để hiển thị những thứ như vậy theo cách có vẻ thuyết phục trên PSP ....
Dragon Energy

Trong khi đó, nếu bạn có các nguyên hàm âm lượng như vậy yêu cầu tự kết xuất, thì các hoạt động của chúng ở mức độ cao đến mức về cơ bản chúng chỉ định hiển thị (điều này không có gì khác biệt ngoại trừ dự phòng bùng binh và khớp nối tiếp), hoặc sự trừu tượng của trình kết xuất không thể thực sự thực hiện mọi thứ theo cách hiệu quả nhất có thể có trên PSP (ít nhất là không thực hiện backflips, điều này sẽ không cần phải làm nếu phụ thuộc bị đảo ngược).
Năng lượng rồng

1
Ok tôi nghĩ rằng tôi đã nhận nó ngay bây giờ. Về cơ bản, bạn đang nói rằng những mối quan tâm như vậy (tức là phần cứng) ở mức độ cao đến mức làm cho các đối tượng cấp thấp như BillBoardphụ thuộc vào chúng sẽ thực sự khó thực hiện? Trong khi một IRenderermức độ cao đã có và có thể phụ thuộc vào những mối quan tâm này với ít rắc rối hơn?
Steve Chamaillard

1
Với những lo ngại về kết xuất tiên tiến trên các nền tảng khác nhau, khả năng phần cứng khác nhau, thật khó để thiết kế một cái gì đó mà tôi là ông chủ và nói cho nó biết phải làm gì. Tôi dễ dàng hơn nhiều khi nói: "Này, tôi là một đám mây / sương mù với những hạt nước màu xám như thế này, và cố gắng làm cho tôi trông ổn, làm ơn đừng chụp cổ tôi mà tôi đã cạo gần đây", và để trình kết xuất tìm ra cách kết xuất tôi theo cách đẹp nhất và thời gian thực nhất có thể với các giới hạn mà nó hoạt động. Bất kỳ nỗ lực nào để nói với nó những gì cần làm gần như chắc chắn sẽ dẫn đến một con đường phản tác dụng.
Năng lượng rồng
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.