2015-06-08 13 views
14

Có vẻ như có một cộng đồng người đang phát triển nói rằng bạn không bao giờ nên trả về giá trị rỗng và phải luôn sử dụng Mẫu đối tượng Null thay thế. Tôi có thể thấy tính hữu ích của NOP khi sử dụng một bộ sưu tập/bản đồ/mảng hoặc gọi các hàm boolean như isAuthenticated(), which is shown here.Mẫu đối tượng Null

Tôi chưa tìm thấy bất kỳ điều gì về điều này hoàn toàn thuyết phục. Hãy cùng tôi ở đây khi tôi cố gắng tổ chức những suy nghĩ của mình.

Sự hiểu biết của tôi là thay vì trả về một đối tượng rỗng, bạn trả về một đối tượng hợp lệ đã được "không".

Vì vậy, ví dụ, khách hàng sẽ thực hiện cuộc gọi để có được một đối tượng:

Car car = getCar(); 

Nếu không sử dụng các NOP bạn sẽ cần phải kiểm tra xem đối tượng trở về từ getCar() là null trước khi gọi bất kỳ phương pháp trên đó:

if (car != null){ 
    color = car.getColor(); 
    doScreenStuff(color); 
    } 

Sử dụng NOP, thay vì getCar() trở về null, bây giờ nó trả về một đối tượng đã được một cách hiệu quả "xóa trắng". Vì vậy, bây giờ chúng tôi không còn cần phải làm if (car != null) và chỉ có thể yêu cầu màu sắc. Vì vậy, tôi cho rằng đối tượng "zeroed out" của chúng ta sẽ trả về "none" khi chúng ta gọi màu.

Điều này giúp ích như thế nào? Dường như việc di chuyển về phía trước và các phương thức gọi trên một đối tượng trống gây ra nhiều đau đớn như chỉ kiểm tra null. Bây giờ, khi đến lúc hiển thị thông tin, chúng ta cần phải kiểm tra xem màu không phải là "không", rằng chiều cao không phải là 0 hoặc bất kỳ giá trị nào khác mà bạn có. Vì vậy, về cơ bản, thay vì kiểm tra trong đầu chế biến nếu chiếc xe là null, bạn kiểm tra sau đó nếu đối tượng xe chúng tôi có là một chiếc xe thực sự hoặc thay thế. I E. chúng tôi không muốn hiển thị một loạt các đối tượng trống, vì vậy chúng tôi cần một số cách để lọc ra tất cả các đối tượng trống của chúng tôi.

Bộ lọc này là một bước bổ sung giống như gọi nếu (car! = Null). Sự khác biệt duy nhất là với việc kiểm tra null, chúng ta có thể ngừng xử lý ngay khi chúng ta khám phá rằng đối tượng ô tô là null bằng cách ném một ngoại lệ, trong khi NOP chúng ta gọi các phương thức trên đối tượng rỗng và tiếp tục chugging cho đến khi nó đến lúc hiển thị đối tượng và tại thời điểm này, chúng tôi lọc ra các ô trống. Hơn nữa, bạn cần phải biết các giá trị được trả về bởi đối tượng trống của bạn. I E. getColor() trả về "none" hoặc "empty".

Rõ ràng phải có thứ tôi đang xem. Cảm ơn trước.

Trả lời

6

Mẫu đối tượng rỗng chỉ có ý nghĩa khi có giá trị hợp lý, chức năng cho đối tượng rỗng. Mục đích không phải là trì hoãn null, như bạn đã mô tả, nhưng để loại bỏ hoàn toàn ý tưởng của null bằng cách biểu diễn sự trống rỗng hoặc không trống rỗng với một đoạn dữ liệu thực tế vẫn còn hoạt động. Ví dụ, trường hợp tự nhiên của các lỗ trong cấu trúc cây, as described in the Wikipedia article.

Một chiếc xe hơi không có ý nghĩa. Trong trường hợp này, có vẻ như điều thích hợp hơn là dành cho getCar() để trả lại Optional<Car>.

0

Đối tượng con trỏ rỗng, ít nhất là theo kinh nghiệm của tôi, vì vậy bạn giới hạn bạn kiểm tra rỗng thành một vị trí trung tâm để tránh ngoại lệ con trỏ null.

Nếu nhiều dịch vụ đang sử dụng CarFactory, sẽ dễ dàng hơn khi Carfactory xử lý kết quả rỗng, sau đó có từng dịch vụ riêng lẻ xử lý nó. Cộng với nó đảm bảo mỗi kết quả null được xử lý cùng một thời trang, cho dù đó là không làm gì hoặc một số quy định logic.Nhược điểm là nếu nó không được xử lý một cách chính xác, nó có thể dẫn đến một số lỗi tạm thời khó hiểu (đặc biệt là vì các ngoại lệ con trỏ null hét to và tự hào).

Tôi không thực sự sử dụng nó nhiều nữa. Có các lựa chọn thay thế để sử dụng kiểm tra null, chẳng hạn như sử dụng Java 8 Tùy chọn. Có những người cho và chống lại điều này là tốt, và điều này là do không có nghĩa là một sự thay thế cho mẫu đối tượng null.

String result = Optional.ofNullable(someInteger) 
    .map(Integer::valueOf) 
    .map(i -> i + 1) 
    .map(Object::toString) 
    .orElse("number not present"); 

System.out.println(result); 
+0

Mẫu đối tượng rỗng và tùy chọn không phân phát cùng một mục đích. Tùy chọn được sử dụng để cho biết rằng một giá trị có thể có hoặc không có mặt. Mẫu đối tượng null chỉ đơn giản là mã hóa thực tế rằng có thể có một đối tượng 'không có gì' mà vẫn có hành vi hữu ích. – Cubic

+0

Xin lỗi, tôi có thể không tự làm rõ, tôi đã nói thay thế cho kiểm tra null là tùy chọn, và sau đó "điều này không có nghĩa là thay thế cho mẫu đối tượng null" cho mẫu đối tượng null. Tôi đã không sử dụng mẫu đối tượng null trong một thời gian vì tôi đã không có một trường hợp sử dụng gần đây, nhưng tôi không có nghĩa là gợi ý rằng các tùy chọn thay thế mẫu đối tượng null. –

3

Nếu bạn không thấy điểm, thì có thể đó không phải là mô hình tốt để sử dụng cho bạn. Toàn bộ ý tưởng về lập trình OO là làm cho mọi việc đơn giản hơn cho BẠN. Đừng bị mắc kẹt vào suy nghĩ bạn cần phải áp dụng một người nào khác xây dựng hệ thống dựa trên mô hình. Thường thì phải mất một số lượng đáng kể công việc để tìm hiểu các mẫu khác nhau và sử dụng chúng một cách hiệu quả, vì vậy tốt hơn là phát triển chúng, thay vì cố gắng ép buộc bản thân sử dụng nó.

Theo như mô hình cụ thể này là có liên quan, nó giả định một kiểu lập trình nhất định có thể không phù hợp với bạn. Tôi sẽ không bao giờ sử dụng nó bản thân mình bởi vì tôi trả về null như giá trị hợp pháp (thiếu dữ liệu) được xử lý khác nhau trong mỗi trường hợp, do đó, "xử lý tập trung" không có ý nghĩa đối với tôi. Khi tôi trả về boolean, tôi sử dụng nguyên thủy.

Điểm mấu chốt ở đây là nếu một mẫu có vẻ không tự nhiên với bạn, đừng sử dụng nó.

+0

Tôi nghĩ bạn nói đúng. Tôi luôn quá lo lắng về việc sử dụng mẫu sai khi lập trình. Trong trường hợp này, dường như có những người cho rằng bạn đang làm mọi thứ hoàn toàn sai nếu bạn trả về null: http://www.yegor256.com/2014/05/13/why-null-is-bad.html – TigerBear

6

Câu trả lời của MattPutnam là đúng trên điểm và tôi xếp thứ hai. Tôi sẽ thêm điều này: khái niệm "đối tượng rỗng", khi bạn phân tích nó, dường như đun sôi xuống khái niệm toán học của một monoid. Bạn có thể nghĩ về nó theo cách này: a monoid là một loại mà có cả những điều này:

  1. An "thêm", "tổng" hoặc hoạt động tương tự, mà cần phải được kết: a.op(b).op(c) là như nhau là a.op(b.op(c)).
  2. Giá trị "trống", "không" hoặc "không", hoạt động như yếu tố trung tính yếu tố trung tính hoặc danh tính của hoạt động.

Ví dụ cổ điển của mẫu đối tượng rỗng là trả lại danh sách hoặc mảng trống thay vì null. Vâng, danh sách là một monoid, với phụ thêm như hoạt động và danh sách trống là yếu tố trung lập.

Bây giờ, sự cố mà bạn gặp phải trong ví dụ CarCar thực sự không phải là một monoid; không có khái niệm về "chiếc xe trống" hay "chiếc xe trung tính", và không thực sự là một hoạt động hợp lý mà bạn có thể sử dụng để kết hợp hai số Car vào một.

Vì vậy, đề xuất bạn đang nhận được là sử dụng một cái gì đó như Java 8 Optional. Và các trick là không có vấn đề gì loại T là, Optional<T> là một monoid:

  1. của monoid "kết hợp" hoạt động là "chọn giá trị đầu tiên nếu nó không empty, nếu không chọn giá trị thứ hai":
    • x || empty = x
    • empty || x = x
  2. Yếu tố trung lập là Optional.empty(), vì Optional.empty().orElse(anything) cũng giống như chỉ anything.

Vì vậy, về cơ bản, Optional<T> là trình bao bọc thêm đối tượng null vào các loại như Car không có. Phương pháp Optional<T>.orElse(T value) là phiên bản được cấu trúc lại một chút của giá trị "chọn đầu tiên không phải là empty" monoid.

0

Mẫu thiết kế NULL điền ABSENCE của đối tượng có hành vi DEFAULT và chỉ được sử dụng khi một đối tượng cộng tác với nhau.

Mẫu thiết kế NULL không có nghĩa là thay thế xử lý ngoại lệ NULL. Đó là một trong những lợi ích phụ của mẫu thiết kế NULL nhưng ý định là cung cấp một hành vi mặc định.

Ví dụ: xem mã mẫu pseduo bên dưới. Một lớp khách hàng đơn giản của nó là ủy nhiệm tính toán chiết khấu cho một đối tượng giảm giá.

class Customer { 
    IDiscount dis = new NormalDiscount(); 
    public double CalculateDiscount() { 
     return dis.Calculate(); 
    } 
    // Code removed for simplicity 
} 

Bây giờ, hãy cho chúng tôi biết rằng chúng tôi muốn tạo khách hàng đã mặc định thanh toán. Vì vậy, chúng tôi kế thừa từ các lớp trên như nhiều tài sản và hành vi giống nhau nhưng chúng tôi không muốn tính toán chiết khấu bởi vì một khách hàng mặc định không đủ điều kiện để được giảm giá. Vì vậy, chúng tôi làm cho đối tượng giảm giá null nhưng đó là một vấn đề bởi vì bây giờ việc tính toán giảm giá sẽ sụp đổ vì đối tượng giảm giá NULL.

class DefaultedCustomer : Customer { 
    IDiscount dis = null; 
    public double CalculateDiscount() { 
     return dis.Calculate(); <---- This will crash in parent 
    } 
    // Code removed for simplicity 
} 

Vì vậy, một trong những cách mà nhà phát triển khắc phục sự cố này là việc kiểm tra NULL và trả về số không. Nếu bạn nhắm mắt lại và suy nghĩ một cách logic thì đây thực sự là kịch bản kinh doanh, nơi không có giảm giá cho khách hàng mặc định.

Vì vậy, chứ không phải là cố định này về mặt kỹ thuật bằng cách kiểm tra NULL chúng ta có thể tạo ra một lớp hành vi mặc định Giảm như hình dưới đây trả về zero giảm giá và sử dụng tương tự cho khách hàng định sẵn. Điều này là sạch hơn so với kiểm tra NULLs.

public class DefaultDiscount : IDiscount { 
    public void Calculate() { 
     return 0; 
    } 
} 

NULL là cần thiết nhưng để xử lý ngoại lệ không phải để sửa không có đối tượng cộng tác như được hiển thị ở trên. Mẫu thiết kế NULL không có ý nghĩa nếu không có sự cộng tác.

Do mẫu thiết kế NULL kiểm tra NULL có thể tránh được nhưng đó là nhiều hơn sau hiệu ứng và lợi ích phụ nhưng không phải là ý định chính.

Tất cả những suy nghĩ trên tôi đã lấy từ điều này NULL Design pattern in C# giải thích Do và Donts of NULL.

-1

Trả lại tùy chọn trên xe sẽ không loại bỏ bước kiểm tra xem đối tượng xe có thực sự hiện diện hay không.

Khi quay lại xe, bạn sẽ kiểm tra xem (xe! = Null). Tương tự khi truy cập Tùy chọn, bạn nên kiểm tra xem (car.isPresent()) sau đó car.get().

Thực hiện lệnh get() mà không kiểm tra sự hiện diện không được chấp nhận và có thể dễ dàng xác định và tránh sử dụng cấu hình kiểu dấu kiểm để phát hiện lỗi.

Bạn có thể đặt câu hỏi .. Lợi thế nào chúng tôi đang nhận được vì điều này nếu chúng tôi đang kiểm tra sự hiện diện của nó trong cả hai mẫu?

Câu trả lời nằm ở việc biết rằng bạn phải thực hiện kiểm tra trước khi sử dụng.Nếu bạn thực hiện nghiêm túc việc thực hành trả về Tùy chọn ở bất cứ nơi nào nó có thể được vô hiệu hóa, thì bạn không cần phải kiểm tra các giá trị rỗng bất cứ nơi nào nó không phải là tùy chọn.

Nó hoạt động như một phương pháp lập trình các phương pháp làm tài liệu giúp nội bộ thực thi kiểm tra thông qua các kiểu kiểm tra.

+0

Tùy chọn không giống với Mẫu đối tượng Null (https://en.wikipedia.org/wiki/Null_Object_pattern) –

+0

Sử dụng 'Tùy chọn' chỉ với' isPresent() 'và' get() 'giống như chỉ đi 20mph trong thương hiệu mới Lamborghini. Điều tốt hơn để làm trong tình huống này là 'getCar(). IfPresent (car -> doScreenStuff (car.getColor()))' – Michael

Các vấn đề liên quan