Là khớp nối với các chuỗi Looser rắn hơn so với các phương thức lớp?


18

Tôi đang bắt đầu một dự án nhóm trường học bằng Java, sử dụng Swing. Đó là một GUI đơn giản trên ứng dụng máy tính để bàn cơ sở dữ liệu.

Giáo sư đã cho chúng tôi mã từ dự án năm ngoái để chúng tôi có thể thấy cách anh ấy làm mọi việc. Ấn tượng ban đầu của tôi là mã phức tạp hơn nhiều so với nó, nhưng tôi tưởng tượng các lập trình viên thường nghĩ điều này khi nhìn vào mã họ không chỉ viết.

Tôi hy vọng tìm thấy lý do tại sao hệ thống của anh ta tốt hay xấu. (Tôi đã hỏi giáo sư và anh ấy nói tôi sẽ thấy sau đó tại sao nó tốt hơn, điều đó không làm tôi hài lòng.)

Về cơ bản, để tránh bất kỳ sự ghép nối nào giữa các đối tượng, mô hình (logic nghiệp vụ) và các khung nhìn bền vững của anh ấy, mọi thứ đều được thực hiện bằng các chuỗi. Các đối tượng bền vững được lưu trữ trong cơ sở dữ liệu là các hàm băm của chuỗi và các mô hình và chế độ xem "đăng ký" với nhau, cung cấp các khóa chuỗi cho "sự kiện" mà chúng đăng ký.

Khi một sự kiện được kích hoạt, chế độ xem hoặc mô hình sẽ gửi một chuỗi tới tất cả những người đăng ký quyết định những việc cần làm cho sự kiện đó. Chẳng hạn, trong một trong các phương thức nghe hành động xem (tôi tin rằng điều này chỉ đặt bikeMakeField trên đối tượng bền vững):

    else if(evt.getSource() == bicycleMakeField)
    {
        myRegistry.updateSubscribers("BicycleMake", bicycleMakeField.getText());
    }

Cuộc gọi đó cuối cùng đã đến phương thức này trong mô hình Xe:

public void stateChangeRequest(String key, Object value) {

            ... bunch of else ifs ...

    else
    if (key.equals("BicycleMake") == true)
    {
                ... do stuff ...

Giáo sư nói rằng cách làm việc này có thể mở rộng và duy trì được nhiều hơn so với quan điểm chỉ đơn giản là gọi một phương thức trên một đối tượng logic nghiệp vụ. Ông nói rằng không có sự kết hợp giữa các quan điểm và mô hình vì họ không biết về sự tồn tại của nhau.

Tôi nghĩ rằng đây là một loại khớp nối tồi tệ hơn, bởi vì khung nhìn và mô hình phải sử dụng cùng một chuỗi để hoạt động. Nếu bạn xóa chế độ xem hoặc mô hình hoặc tạo lỗi chính tả trong chuỗi, bạn sẽ không gặp lỗi biên dịch. Nó cũng làm cho mã dài hơn rất nhiều so với tôi nghĩ nó cần phải có.

Tôi muốn thảo luận điều này với anh ta, nhưng anh ta sử dụng kinh nghiệm trong ngành của mình để chống lại bất kỳ cuộc tranh luận nào mà tôi, một sinh viên thiếu kinh nghiệm, có thể đưa ra. Có một số lợi thế cho cách tiếp cận của anh ấy mà tôi đang thiếu?


Để làm rõ, tôi muốn so sánh cách tiếp cận trên, với việc có quan điểm rõ ràng được kết hợp với mô hình. Chẳng hạn, bạn có thể chuyển đối tượng mô hình xe sang chế độ xem và sau đó để thay đổi "chế tạo" của phương tiện, hãy làm điều này:

vehicle.make = bicycleMakeField.getText();

Điều này sẽ giảm 15 dòng mã hiện CHỈ được sử dụng để đặt chế tạo của chiếc xe ở một nơi, thành một dòng mã có thể đọc được. (Và vì loại hoạt động này được thực hiện hàng trăm lần trong suốt ứng dụng, tôi nghĩ rằng nó sẽ là một chiến thắng lớn cho khả năng đọc cũng như an toàn.)


Cập nhật

Trưởng nhóm của tôi và tôi đã cấu trúc lại khung theo cách chúng tôi muốn thực hiện bằng cách sử dụng kiểu gõ tĩnh, thông báo cho giáo sư và cuối cùng đưa cho anh ta bản demo. Anh ấy đủ hào phóng để cho chúng tôi sử dụng khuôn khổ của mình miễn là chúng tôi không yêu cầu anh ấy giúp đỡ, và liệu chúng tôi có thể giữ cho phần còn lại của nhóm chúng tôi tăng tốc - điều đó có vẻ công bằng với chúng tôi.


12
Cựu ước. Tôi nghĩ giáo sư của bạn nên tự cập nhật và không sử dụng mã cũ. Trong giới hàn lâm, không có lý do gì để sử dụng phiên bản JAVA từ năm 2006 khi đó là năm 2012.
Nông dân

3
Hãy giữ cho chúng tôi được đăng khi cuối cùng bạn nhận được câu trả lời của anh ấy.
JeffO

4
Bất kể chất lượng của mã hoặc sự khôn ngoan của kỹ thuật, bạn nên bỏ từ "ma thuật" khỏi mô tả của các chuỗi được sử dụng làm định danh. "Chuỗi ma thuật" ngụ ý rằng hệ thống không logic và nó sẽ tô màu cho quan điểm của bạn và đầu độc mọi cuộc thảo luận bạn có với người hướng dẫn. Các từ như "khóa", "tên" hoặc "số nhận dạng" trung tính hơn, có thể mô tả nhiều hơn và có nhiều khả năng dẫn đến một cuộc trò chuyện hữu ích.
Caleb

1
Thật. Tôi đã không gọi họ là những chuỗi ma thuật trong khi nói chuyện với anh ta, nhưng bạn nói đúng, không cần phải làm cho nó nghe tệ hơn thế.
Philip

2
Cách tiếp cận mà giáo sư của bạn mô tả (thông báo cho người nghe thông qua các sự kiện được đặt tên là chuỗi) có thể tốt. Trên thực tế enums có thể có ý nghĩa hơn, nhưng đây không phải là vấn đề lớn nhất. Vấn đề thực sự tôi thấy ở đây là đối tượng nhận thông báo là chính mô hình, và sau đó bạn phải gửi thủ công dựa trên loại sự kiện. Trong một ngôn ngữ mà các hàm là lớp đầu tiên, bạn sẽ đăng ký một hàm , không phải là một đối tượng cho một sự kiện. Trong Java, tôi đoán người ta nên sử dụng một số loại đối tượng bao bọc một hàm duy nhất
Andrea

Câu trả lời:


32

Cách tiếp cận mà giáo sư của bạn đề xuất được mô tả tốt nhất là đánh máy nghiêm ngặt và sai ở hầu hết mọi cấp độ.

Khớp nối được giảm tốt nhất bằng nghịch đảo phụ thuộc , điều này được thực hiện bằng một công văn đa hình, thay vì làm cứng một số trường hợp.


3
Bạn viết "gần như mọi cấp độ", nhưng bạn chưa viết tại sao nó không (theo ý kiến ​​của bạn) một trường hợp phù hợp trong ví dụ này. Sẽ rất thú vị khi biết.
hakre

1
@hakre: Không có trường hợp nào phù hợp, ít nhất là không có trong Java. Tôi nói, nó "sai ở hầu hết mọi cấp độ", bởi vì nó tốt hơn là không sử dụng bất cứ sự gián tiếp nào và chỉ ném tất cả mã vào một nơi. Tuy nhiên, việc sử dụng các hằng số chuỗi ma thuật (toàn cầu) làm cơ sở cho việc gửi thủ công chỉ là một cách sử dụng các đối tượng toàn cầu cuối cùng.
back2dos

Vì vậy, lập luận của bạn là: Không bao giờ có trường hợp phù hợp, vì vậy đây cũng không phải là một trường hợp? Đó không phải là một cuộc tranh luận và thực sự không hữu ích.
hakre

1
@hakre: Lập luận của tôi là, cách tiếp cận này không tốt vì một số lý do (đoạn 1 - đủ lý do để tránh điều này được đưa ra trong phần giải thích về cách gõ chuỗi) và không nên sử dụng, vì có một lý do tốt hơn (đoạn 2 ).
back2dos

3
@ hh Nói tóm lại, dựa vào các tính năng ngôn ngữ để thực hiện loại việc này là thích hợp hơn. Trên thực tế, tôi có thể nghĩ về một trường hợp khác liên quan đến cá sấu và ninja, nhưng đó là một chút liên quan.
Rob

19

Giáo sư của bạn đang làm sai . Anh ta đang cố gắng tách hoàn toàn khung nhìn và mô hình bằng cách buộc truyền thông đi qua chuỗi theo cả hai hướng (chế độ xem không phụ thuộc vào mô hình và mô hình không phụ thuộc vào chế độ xem) , điều này hoàn toàn đánh bại mục đích của thiết kế hướng đối tượng và MVC. Nghe có vẻ như thay vì viết các lớp và thiết kế kiến ​​trúc dựa trên OO, anh ta viết các mô-đun khác nhau chỉ đơn giản là trả lời các tin nhắn dựa trên văn bản.

Để công bằng cho giáo sư, ông đang cố gắng tạo ra trong Java trông rất giống với giao diện .NET rất phổ biến được gọi INotifyPropertyChanged, thông báo cho những người đăng ký các sự kiện thay đổi bằng cách chuyển các chuỗi xác định thuộc tính nào đã thay đổi. Trong .NET ít nhất, giao diện này chủ yếu được sử dụng để giao tiếp từ một mô hình dữ liệu để xem các đối tượng và các thành phần GUI (ràng buộc).

Nếu được thực hiện đúng , thực hiện phương pháp này có thể có một số lợi ích¹:

  1. Chế độ xem tùy thuộc vào Mô hình, nhưng Mô hình không cần phụ thuộc vào Chế độ xem. Đây là Inversion of Control thích hợp .
  2. Bạn có một vị trí duy nhất trong mã View của mình để quản lý phản hồi thay đổi thông báo từ mô hình và chỉ có một nơi để đăng ký / hủy đăng ký, giảm khả năng rò rỉ bộ nhớ tham chiếu treo.
  3. Có ít chi phí mã hóa khi bạn muốn thêm hoặc xóa sự kiện khỏi nhà xuất bản sự kiện. Trong .NET, có một cơ chế xuất bản / đăng ký tích hợp được gọi eventsnhưng trong Java, bạn sẽ phải tạo các lớp hoặc đối tượng và viết mã đăng ký / hủy đăng ký cho mỗi sự kiện.
  4. Nhược điểm lớn nhất với cách tiếp cận này là mã đăng ký có thể không đồng bộ với mã xuất bản. Tuy nhiên, đây cũng là một lợi ích trong một số môi trường phát triển. Nếu một sự kiện mới được phát triển trong mô hình và chế độ xem chưa được cập nhật để đăng ký vào nó, thì chế độ xem vẫn có thể hoạt động. Tương tự, nếu một sự kiện bị xóa, bạn có một số mã chết, nhưng nó vẫn có thể biên dịch và chạy.
  5. Trong .NET ít nhất, Reflection được sử dụng rất hiệu quả ở đây để tự động gọi getters và setters tùy thuộc vào chuỗi được truyền cho thuê bao.

Vì vậy, trong ngắn hạn (và để trả lời câu hỏi của bạn), có thể thực hiện được, nhưng cách tiếp cận mà giáo sư của bạn đang thực hiện có lỗi vì không cho phép ít nhất một sự phụ thuộc một chiều giữa Chế độ xem và Mô hình. Nó chỉ không thực tế, quá hàn lâm và không phải là một ví dụ tốt về cách sử dụng các công cụ trong tay.


Tìm kiếm trên Google INotifyPropertyChangedđể tìm một triệu cách mọi người tìm cách tránh sử dụng chuỗi ma thuật khi triển khai nó.


Có vẻ như bạn đã mô tả SOAP ở đây. : Dẻ (nhưng vâng, tôi hiểu ý của bạn và bạn nói đúng)
Konrad Rudolph

11

enums (hoặc public static final Strings) nên được sử dụng thay cho Chuỗi ma thuật.

Giáo sư của bạn không hiểu OO . Ví dụ có một lớp Xe cụ thể?
Và để có lớp học này hiểu về việc tạo ra một chiếc xe đạp?

Xe nên trừu tượng và nên có một lớp con cụ thể gọi là Bike. Tôi sẽ chơi theo luật của anh ấy để đạt điểm cao và sau đó quên đi việc dạy học của anh ấy.


3
Trừ khi mục đích của bài tập này là cải thiện mã bằng các nguyên tắc OO tốt hơn. Trong trường hợp đó, tái cấu trúc đi.
joshin4colours

2
@ joshin4colours Nếu StackExchange chấm điểm thì bạn sẽ đúng; nhưng vì học sinh là cùng một người đã đưa ra ý tưởng nên anh ta cho tất cả chúng ta những điểm xấu vì anh ta biết việc thực hiện của mình tốt hơn.
Dan Neely

7

Tôi nghĩ rằng bạn có một điểm tốt, chuỗi ma thuật là xấu. Một người nào đó trong nhóm của tôi đã phải gỡ lỗi một vấn đề gây ra bởi chuỗi ma thuật vài tuần trước (nhưng đó là trong mã riêng của họ mà họ đã viết nên tôi giữ im lặng). Và nó đã xảy ra ... trong công nghiệp !!

Lợi thế cho cách tiếp cận của ông là mã có thể nhanh hơn, đặc biệt là đối với các dự án demo nhỏ. Nhược điểm là nó dễ bị lỗi chính tả, chuỗi càng được sử dụng thường xuyên thì càng có nhiều khả năng mã lỗi chính tả làm hỏng mọi thứ. Và nếu bạn muốn thay đổi chuỗi ma thuật, tái cấu trúc chuỗi khó hơn tái cấu trúc biến vì các công cụ tái cấu trúc cũng có thể chạm vào chuỗi có chứa chuỗi ma thuật (dưới dạng chuỗi con) nhưng không phải là chuỗi ma thuật và không nên chạm vào. Ngoài ra, bạn có thể cần giữ một từ điển hoặc chỉ mục các chuỗi ma thuật để các nhà phát triển mới sẽ không bắt đầu phát minh ra các chuỗi ma thuật mới cho cùng một mục đích và sẽ không lãng phí thời gian để tìm kiếm các chuỗi ma thuật hiện có.

Trong bối cảnh này, có vẻ như một Enum có thể tốt hơn các chuỗi ma thuật hoặc thậm chí các hằng chuỗi toàn cầu. Ngoài ra, các IDE thường sẽ cung cấp cho bạn một số loại hỗ trợ mã (tự động hoàn thành, tái cấu trúc, tìm tất cả các tham chiếu, v.v.) khi bạn sử dụng các chuỗi hoặc Enums không đổi. Một IDE sẽ không cung cấp nhiều trợ giúp nếu bạn sử dụng chuỗi ma thuật.


Tôi đồng ý rằng enums tốt hơn chuỗi ký tự. Nhưng ngay cả khi đó, có thực sự tốt hơn khi gọi một "stateChangeRequest" bằng khóa enum hoặc chỉ gọi trực tiếp một phương thức trên mô hình? Dù bằng cách nào, mô hình và khung nhìn được ghép nối tại một điểm đó.
Philip

@Philip: Chà, tôi đoán là để tách mô hình và xem tại thời điểm đó, bạn có thể cần một số loại trình điều khiển để làm điều đó ...
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner - Có. Một kiến ​​trúc MVC là phần tách rời nhất
cdeszaq

Phải, nếu một lớp khác sẽ giúp tách chúng ra, tôi sẽ không tranh cãi về điều đó. Nhưng lớp đó vẫn có thể được gõ tĩnh và sử dụng các phương thức thực tế thay vì các thông điệp chuỗi hoặc enum để kết nối chúng.
Philip

6

Sử dụng 'kinh nghiệm trong ngành' là một lập luận kém. Giáo sư của bạn cần đưa ra một lập luận tốt hơn thế :-).

Như những người khác đã tuyên bố, việc sử dụng các chuỗi ma thuật chắc chắn được tán thành, sử dụng enums được coi là an toàn hơn nhiều. Một enum có ý nghĩaphạm vi , chuỗi ma thuật thì không. Bên cạnh đó, cách mà giáo sư của bạn đang tách rời mối quan tâm được coi là một kỹ thuật cũ và mỏng manh hơn so với sử dụng ngày nay.

Tôi thấy rằng @backtodos đã trình bày vấn đề này trong khi tôi trả lời câu hỏi này, vì vậy tôi sẽ chỉ thêm ý kiến ​​của mình rằng sử dụng Inversion of Control (trong đó Dependency Injection là một hình thức, bạn sẽ chạy trong khuôn khổ Spring trong ngành từ lâu. ..) được coi là một cách hiện đại hơn để đối phó với kiểu tách rời này.


2
Học viện hoàn toàn đồng ý nên có yêu cầu cao hơn nhiều so với ngành công nghiệp. Học viện == cách lý thuyết tốt nhất; Ngành công nghiệp == cách thực tế tốt nhất
Nông dân

3
Thật không may, một số người dạy những thứ này trong học viện làm như vậy bởi vì họ không thể cắt nó trong công nghiệp. Về mặt tích cực, một số người trong giới hàn lâm rất thông minh và không nên giấu diếm trong một số văn phòng công ty.
Thất vọngWithFormsDesigner

4

Chúng ta hãy khá rõ ràng; có chắc chắn một số khớp nối trong đó. Thực tế là có một chủ đề có thể đăng ký như vậy là một điểm của sự kết hợp, như bản chất của phần còn lại của thông điệp (chẳng hạn như chuỗi đơn đơn Hồi). Do đó, câu hỏi sau đó trở thành một trong những liệu khớp nối có lớn hơn khi được thực hiện thông qua chuỗi hoặc loại. Câu trả lời cho điều đó phụ thuộc vào việc bạn quan tâm đến việc ghép được phân phối theo thời gian hay không gian: nếu các tin nhắn sẽ được lưu giữ trong một tệp trước khi được đọc sau đó, hoặc sẽ được gửi đến một quy trình khác (đặc biệt là nếu điều đó không được viết bằng cùng một ngôn ngữ), sử dụng các chuỗi có thể làm cho mọi thứ đơn giản hơn nhiều. Nhược điểm là không có loại kiểm tra; lỗi ngớ ngẩn sẽ không được phát hiện cho đến khi thời gian chạy (và đôi khi thậm chí không sau đó).

Chiến lược giảm thiểu / thỏa hiệp cho Java có thể là sử dụng giao diện được gõ, nhưng giao diện được gõ đó nằm trong một gói riêng. Nó chỉ bao gồm các interfaceloại giá trị cơ bản và Java (ví dụ: liệt kê, ngoại lệ). Nên có không triển khai các giao diện trong gói đó. Sau đó, mọi người đều thực hiện để chống lại điều đó và, nếu việc truyền tải các thông điệp theo một cách phức tạp là cần thiết, một triển khai cụ thể của một đại biểu Java có thể sử dụng các chuỗi ma thuật khi cần thiết. Nó cũng giúp việc di chuyển mã đến một môi trường chuyên nghiệp hơn như JEE hoặc OSGi dễ dàng hơn rất nhiều và hỗ trợ rất nhiều cho những việc nhỏ như thử nghiệm


4

Những gì giáo sư của bạn đang đề xuất là việc sử dụng "chuỗi ma thuật". Trong khi nó thực hiện các lớp "lỏng lẻo" giữa nhau, cho phép thay đổi dễ dàng hơn, đó là một mô hình chống; Các chuỗi rất, RẤT hiếm khi được sử dụng để chứa hoặc kiểm soát các lệnh mã và khi điều đó hoàn toàn xảy ra, giá trị của các chuỗi bạn đang sử dụng để điều khiển logic phải được tập trung và liên tục để có một quyền đối với giá trị mong đợi của bất kỳ chuỗi cụ thể.

Lý do tại sao rất đơn giản; các chuỗi không được kiểm tra bởi trình biên dịch về cú pháp hoặc tính nhất quán với các giá trị khác (tốt nhất, chúng được kiểm tra về hình thức phù hợp liên quan đến các ký tự thoát và định dạng ngôn ngữ cụ thể khác trong chuỗi). Bạn có thể đặt bất cứ thứ gì bạn muốn trong một chuỗi. Như vậy, bạn có thể có được một thuật toán / chương trình bị hỏng vô vọng để biên dịch, và phải đến khi nó chạy thì xảy ra lỗi. Hãy xem xét các mã sau (C #):

private Dictionary<string, Func<string>> methods;

private void InitDictionary() //called elsewhere
{
   methods = new Dictionary<string, Func<string>> 
      {{"Method1", ()=>doSomething()},
       {"MEthod2", ()=>doSomethingElse()}} //oops, fat-fingered it
}

public string RunMethod(string methodName)
{
   //very naive, but even a check for the key would generate a runtime error, 
   //not a compile-time one.
   return methods[methodName](); 
}

...

//this is what we thought we entered back in InitDictionary for this method...
var result = RunMethod("Method2"); //error; no such key

... tất cả các mã này biên dịch, thử lần đầu tiên, nhưng lỗi là rõ ràng với cái nhìn thứ hai. Có rất nhiều ví dụ về loại lập trình này và tất cả chúng đều phải chịu loại lỗi này, NGAY CẢ NẾU bạn xác định các chuỗi là hằng số (vì các hằng số trong .NET được ghi vào bảng kê khai của mỗi thư viện mà chúng được sử dụng, phải sau đó tất cả được biên dịch lại khi thay đổi giá trị được xác định để giá trị thay đổi "toàn cầu"). Vì lỗi không được phát hiện tại thời gian biên dịch, nó phải được phát hiện trong quá trình kiểm tra thời gian chạy và điều đó chỉ được đảm bảo với độ bao phủ 100% mã trong các thử nghiệm đơn vị với bài tập mã đầy đủ, thường chỉ được tìm thấy trong trường hợp không an toàn tuyệt đối nhất , hệ thống thời gian thực.


2

Trên thực tế, những lớp học vẫn được liên kết chặt chẽ. Ngoại trừ bây giờ chúng được liên kết chặt chẽ theo cách mà trình biên dịch không thể cho bạn biết khi mọi thứ bị hỏng!

Nếu ai đó thay đổi "Xe đạp" thành "Xe đạp", thì không ai sẽ phát hiện ra rằng mọi thứ bị hỏng cho đến khi chạy.

Lỗi thời gian chạy là cách tốn kém hơn để sửa chữa so với lỗi thời gian biên dịch - đây chỉ là tất cả các loại ác.

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.