Khi nào bạn nên và không nên sử dụng từ khóa 'mới'?


15

Tôi đã xem một bài thuyết trình của Google Tech Talk về Kiểm tra đơn vị , được đưa ra bởi Misko Hevery và anh ấy nói để tránh sử dụng newtừ khóa trong mã logic kinh doanh.

Tôi đã viết một chương trình và cuối cùng tôi đã sử dụng newtừ khóa ở đây và ở đó, nhưng chúng chủ yếu là để khởi tạo các đối tượng chứa dữ liệu (nghĩa là chúng không có bất kỳ chức năng hoặc phương thức nào).

Tôi tự hỏi, tôi đã làm gì sai khi tôi sử dụng từ khóa mới cho chương trình của mình. Và chúng ta có thể phá vỡ 'quy tắc' đó ở đâu?


2
Bạn có thể thêm một thẻ liên quan đến ngôn ngữ lập trình? Mới tồn tại trong nhiều ngôn ngữ (C ++, Java, Ruby để đặt tên cho một số ngôn ngữ) và có ngữ nghĩa khác nhau.
sakisk

Câu trả lời:


16

Đây là hướng dẫn nhiều hơn quy tắc khó và nhanh.

Bằng cách sử dụng "mới" trong mã sản xuất của bạn, bạn đang ghép lớp của mình với các cộng tác viên của nó. Nếu ai đó muốn sử dụng một số cộng tác viên khác, ví dụ như một loại cộng tác viên giả để thử nghiệm đơn vị, họ không thể - vì cộng tác viên được tạo trong logic kinh doanh của bạn.

Tất nhiên, ai đó cần phải tạo các đối tượng mới này, nhưng điều này thường được để lại ở một trong hai vị trí: khung tiêm phụ thuộc như Spring hoặc bất kỳ lớp nào đang khởi tạo lớp logic nghiệp vụ của bạn, được đưa vào qua hàm tạo.

Tất nhiên, bạn có thể đưa điều này quá xa. Nếu bạn muốn trả về một ArrayList mới, thì có lẽ bạn vẫn ổn - đặc biệt nếu đây sẽ là một Danh sách bất biến.

Câu hỏi chính bạn nên tự hỏi mình là "trách nhiệm chính của bit mã này là tạo ra các đối tượng thuộc loại này, hay đây chỉ là một chi tiết thực hiện mà tôi có thể di chuyển hợp lý ở nơi khác?"


1
Vâng, nếu bạn muốn nó là một danh sách bất biến, bạn nên sử dụng Collections.unmodifiableListhoặc một cái gì đó. Nhưng tôi biết ý của bạn là gì :)
MatrixFrog

Có, nhưng bạn cần bằng cách nào đó để tạo danh sách ban đầu mà sau đó bạn chuyển đổi thành không thể thay đổi ...
Bill Michell

5

Mấu chốt của câu hỏi này là ở cuối: Tôi tự hỏi, tôi đã làm gì sai khi tôi sử dụng từ khóa mới cho chương trình của mình. Và chúng ta có thể phá vỡ 'quy tắc' đó ở đâu?

Nếu bạn có thể viết các bài kiểm tra đơn vị hiệu quả cho mã của mình, thì bạn không làm gì sai. Nếu việc sử dụng của newbạn gây khó khăn hoặc không thể đơn vị kiểm tra mã của bạn, thì bạn nên đánh giá lại việc sử dụng mới của bạn. Bạn có thể mở rộng phân tích này để tương tác với các lớp khác, nhưng khả năng viết các bài kiểm tra đơn vị vững chắc thường là một proxy đủ tốt.


5

Nói tóm lại, bất cứ khi nào bạn sử dụng "mới", bạn sẽ liên kết chặt chẽ lớp chứa mã này với đối tượng được tạo; để khởi tạo một trong các đối tượng này, lớp thực hiện khởi tạo phải biết về lớp cụ thể được khởi tạo. Vì vậy, khi sử dụng "mới", bạn nên xem xét liệu lớp bạn đang đặt khởi tạo có phải là nơi "tốt" để kiến ​​thức đó cư trú hay không và bạn có sẵn sàng thay đổi trong lĩnh vực này hay không nếu hình thức của đối tượng được khởi tạo là để thay đổi.

Khớp nối chặt chẽ, đó là một đối tượng có kiến ​​thức về một lớp cụ thể khác, không phải lúc nào cũng phải tránh; Ở một mức độ nào đó, một cái gì đó, SOMEWHERE, phải biết cách tạo ra đối tượng này, ngay cả khi mọi thứ khác liên quan đến đối tượng bằng cách được cung cấp một bản sao của nó từ một nơi khác. Tuy nhiên, khi lớp được tạo thay đổi, bất kỳ lớp nào biết về việc triển khai cụ thể của lớp đó phải được cập nhật để xử lý chính xác các thay đổi của lớp đó.

Câu hỏi mà bạn luôn phải đặt ra là "Liệu lớp này có biết cách tạo lớp khác này trở thành trách nhiệm khi duy trì ứng dụng không?" Cả hai phương pháp thiết kế chính (RẮN và GRASP) thường sẽ trả lời "có", vì những lý do khác nhau. Tuy nhiên, chúng chỉ là phương pháp và cả hai đều có giới hạn cực đoan là chúng không được xây dựng dựa trên kiến ​​thức về chương trình độc đáo của bạn. Như vậy, họ chỉ có thể nhầm lẫn về mặt thận trọng và cho rằng bất kỳ điểm nào của khớp nối chặt chẽ sẽ TUYỆT VỜI khiến bạn gặp vấn đề liên quan đến việc thay đổi một trong hai hoặc của cả hai điểm này. Bạn phải đưa ra quyết định cuối cùng khi biết ba điều; thực hành tốt nhất về mặt lý thuyết (đó là ghép đôi mọi thứ một cách lỏng lẻo vì mọi thứ đều có thể thay đổi); chi phí thực hiện thực tiễn tốt nhất về mặt lý thuyết (có thể bao gồm một số lớp trừu tượng mới sẽ giảm bớt một loại thay đổi trong khi cản trở một loại khác); và xác suất trong thế giới thực rằng loại thay đổi bạn dự đoán sẽ là cần thiết.

Một số hướng dẫn chung:

  • Tránh khớp nối chặt chẽ giữa các thư viện mã được biên dịch. Giao diện giữa các DLL (hoặc EXE và DLL của nó) là nơi chính mà sự kết hợp chặt chẽ sẽ gây ra bất lợi. Nếu bạn thay đổi một lớp A trong DLL X và lớp B trong EXE chính biết về lớp A, bạn phải biên dịch lại và giải phóng cả hai nhị phân. Trong một nhị phân đơn, khớp nối chặt chẽ hơn thường được cho phép hơn vì toàn bộ nhị phân phải được xây dựng lại cho bất kỳ thay đổi nào. Đôi khi, việc phải xây dựng lại nhiều nhị phân là không thể tránh khỏi, nhưng bạn nên cấu trúc mã của mình để có thể tránh được khi có thể, đặc biệt đối với các tình huống băng thông ở mức cao (như triển khai ứng dụng di động; đẩy DLL mới trong nâng cấp rẻ hơn rất nhiều hơn là đẩy toàn bộ chương trình).

  • Tránh khớp nối chặt chẽ giữa các "trung tâm logic" chính của chương trình của bạn. Bạn có thể nghĩ về một chương trình có cấu trúc tốt bao gồm các lát ngang và dọc. Các lát cắt ngang có thể là các tầng ứng dụng truyền thống, như UI, Bộ điều khiển, Miền, DAO, Dữ liệu; lát cắt dọc có thể được xác định cho từng cửa sổ hoặc chế độ xem hoặc cho "câu chuyện người dùng" riêng lẻ (như tạo bản ghi mới của một số loại cơ bản). Khi thực hiện một cuộc gọi di chuyển lên, xuống, trái hoặc phải trong một hệ thống có cấu trúc tốt, bạn thường nên gọi cuộc gọi trừu tượng. Ví dụ, khi xác thực cần truy xuất dữ liệu, nó không nên truy cập trực tiếp vào DB mà nên thực hiện cuộc gọi đến giao diện để truy xuất dữ liệu, được hỗ trợ bởi đối tượng thực tế biết cách thực hiện việc này. Khi một số điều khiển UI cần thực hiện logic nâng cao liên quan đến một cửa sổ khác, cần tóm tắt việc kích hoạt logic này thông qua một sự kiện và / hoặc gọi lại; kết quả là không cần phải biết những gì sẽ được thực hiện, cho phép bạn thay đổi những gì sẽ được thực hiện mà không thay đổi điều khiển kích hoạt nó.

  • Trong mọi trường hợp, hãy xem xét việc thay đổi sẽ dễ dàng hay khó khăn như thế nào, và khả năng thay đổi sẽ như thế nào. Nếu một đối tượng bạn đang tạo chỉ được sử dụng từ một nơi và bạn không thấy trước sự thay đổi đó, thì khớp nối chặt chẽ thường được cho phép hơn, và thậm chí có thể vượt trội hơn trong tình huống này đối với khớp nối lỏng lẻo. Khớp nối lỏng lẻo đòi hỏi sự trừu tượng, là lớp bổ sung ngăn thay đổi đối tượng phụ thuộc khi việc thực hiện phụ thuộc phải thay đổi. Tuy nhiên, nếu bản thân giao diện phải thay đổi (thêm một cuộc gọi phương thức mới hoặc thêm một tham số vào một cuộc gọi phương thức hiện có), thì một giao diện thực sự làm tăng lượng công việc cần thiết để thực hiện thay đổi. Bạn phải cân nhắc khả năng các loại thay đổi khác nhau ảnh hưởng đến thiết kế,


3

Các đối tượng chỉ giữ dữ liệu hoặc DTO (đối tượng truyền dữ liệu) đều ổn, hãy tạo các đối tượng đó cả ngày. Nơi bạn muốn tránh newlà trên các lớp thực hiện hành động, bạn muốn các lớp tiêu thụ thay vì lập trình theo giao diện , nơi chúng có thể gọi logic đó và tiêu thụ nó, nhưng không chịu trách nhiệm thực sự tạo ra các thể hiện của các lớp có logic . Những lớp thực hiện hành động hoặc logic chứa các lớp là phụ thuộc. Cuộc nói chuyện của Misko hướng đến việc bạn tiêm những phụ thuộc đó và để một số thực thể khác chịu trách nhiệm thực sự tạo ra chúng.


2

Những người khác đã đề cập đến điểm này, nhưng muốn trích dẫn điều này từ Bộ luật sạch của chú Bob (Bob Martin) bởi vì nó có thể giúp dễ hiểu khái niệm này hơn:

"Một cơ chế mạnh mẽ để phân tách xây dựng khỏi sử dụng là Dependency Injection (DI), ứng dụng Inversion of Control (IoC) để quản lý phụ thuộc. Đảo ngược điều khiển chuyển trách nhiệm phụ từ một đối tượng sang các đối tượng khác dành riêng cho mục đích, do đó hỗ trợ các đơn trách nhiệm Nguyên tắc . trong bối cảnh quản lý phụ thuộc, một đối tượng không nên chịu trách nhiệm cho instantiating phụ thuộc riêng của mình. Thay vào đó, nó sẽ vượt qua trách nhiệm này sang cơ chế khác "có thẩm quyền", qua đó đảo ngược sự kiểm soát. Bởi vì thiết lập là một mối quan tâm toàn cầu, điều này cơ chế có thẩm quyền thường sẽ là thói quen "chính" hoặc thùng chứa mục đích đặc biệt. "

Tôi nghĩ luôn luôn là một ý tưởng tốt để làm theo quy tắc này. Những người khác đã đề cập đến một IMO quan trọng hơn -> nó tách lớp của bạn khỏi các phụ thuộc (cộng tác viên). Lý do thứ hai là nó làm cho các lớp học của bạn nhỏ hơn và chặt chẽ hơn, dễ hiểu hơn và thậm chí sử dụng lại trong các bối cảnh khá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.