2011-12-04 11 views
5

Tôi có ba lớp, một Người dùng trừu tượng và hai lớp cụ thể: NormalUser chứa một ArrayList của một hoặc nhiều đối tượng Địa chỉ có thể khác nhau (trong nước, quốc tế, tùy chỉnh, v.v.) Lớp quản trị có phương thức trả về true. Cả hai đều chứa nhiều phương pháp không liên quan đến nhau.Java Thừa kế và tránh sử dụng liên tục instanceof

abstract class User{ 
    public User(String username, String pw){ 
... 

} 

public class NormalUser extends User{ 
... 
    private ArrayList<Address> addresses; 

... 

    public void addAdress(ArrayList<Address> address){ 
     addresses.addAll(address); 
} 

public class Admin extends User{ 

... 
    public boolean getIsAdmin(){ 
     return true; 
    } 
} 

Bây giờ trong lớp khác nếu tôi thực hiện 4 đối tượng sử dụng như thế này ví dụ:

ArrayList<User> users; 

    users.add(new NormalUser("1", "pw"); 
    users.add(new NormalUser("2", "pw"); 
    users.add(new NormalUser("3", "pw"); 
    users.add(new NormalUser("4", "pw"); 
    users.add(new Admin("5", "pw")); 
    users.add(new NormalUser("6", "pw"); 

Và nói rằng tôi muốn sử dụng phương pháp addAddress trong NormalUser, sau đó tôi phải downCast người dùng specfic trong người dùng NormalUser, trước khi tôi có thể sử dụng phương pháp addAddress trong NormalUser như thế này:

 if (user instanceof NormalUser){ 
     NormalUser normal = (NormalUser) user; 
     normal.addAddress(...) 
     } 

lý do tại sao tôi muốn cả hai NormalUser và quản lý là một tài khoản là để tôi có thể xử lý chúng cùng nhau khi đăng nhập.

Tôi đã nghĩ việc thêm addEmail vào lớp User và sau đó ghi đè nó trong lớp NormalUser, nhưng tôi sẽ phải làm điều đó cho mọi phương thức trong lớp NormalUser, cộng với Admin sẽ kế thừa nó từ Người dùng là tốt, khi nó không cần chức năng đó.

Câu hỏi 1: Có cách nào tốt hơn để làm điều này như tôi đã nghe bằng cách sử dụng instanceof là một điều xấu? và tôi sẽ phải sử dụng instanceof mỗi khi tôi sử dụng một phương thức dành riêng cho lớp NormalUser.

Quesiton 2: Là một ArrayList của đối tượng Địa chỉ cách tốt nhất để liên kết Người dùng thường xuyên với các địa chỉ cụ thể/(Đối tượng)?

Hiện không có cơ sở dữ liệu nào có liên quan.

Vì vậy, ví dụ sử dụng một có 2 địa chỉ một trong nước và một quốc tế, và người sử dụng b chỉ có một địa chỉ trong nước, sử dụng c có trong nước và địa chỉ tùy chỉnh, vv

Cảm ơn.

PS. Tôi đã tìm kiếm các bài viết trước đây rộng rãi nhưng havent tìm thấy một giải pháp. Trong cả hai cuốn sách Java của tôi cả hai đều cho thấy ví dụ về cách sử dụng instanceof nhưng không đề cập đến nó là một thực hành xấu.

+0

Đối với trường hợp cụ thể này, tôi sợ tôi không thể nói điều nào tốt hơn, nhưng không, 'instanceof' không phải là định nghĩa một điều xấu. Bạn không nên lạm dụng nó khi tính đa hình phù hợp hơn. –

+0

Liên quan đến 'instanceof': bạn nên suy nghĩ lại về thiết kế của mình sao cho bạn không bao giờ gọi' addAddress' trên một 'User' cơ bản. Bạn chỉ nên gọi một phương thức như vậy khi bạn đang ở trong một khối mã được thiết kế đặc biệt cho 'NormalUser'. – toto2

+0

có lý do nào tốt để Quản trị viên không có địa chỉ không? – soulcheck

Trả lời

3

Bạn có thể sử dụng Visitor pattern - hơi vụng về và hơi khó đọc, nhưng có lẽ là giải pháp tốt nhất cho sự cố của bạn.

Thực tế giải pháp của bạn khi đẩy addEmail đến lớp cơ sở không phải là xấu. Chỉ cần cung cấp triển khai trống trong cơ sở User và ghi đè trong RegularUser. Nếu bạn muốn kiểm tra xem cá thể User có hỗ trợ thêm e-mail hay không, hãy cung cấp phương thức khác như supportsAddEmail trả lại false theo mặc định và true khi ghi đè addEmail.

+0

Nhưng wont Admin kế thừa addAddress (ngay cả khi nó rỗng)? – Brah

+0

Có, điều này có nghĩa là bạn có thể gọi 'Admin.addEmail() ', không có gì (no-op). Đó là lý do tại sao tôi đề nghị 'supportAddEmail', nhưng nó không thực sự trông giống như một ý tưởng hay. Bit không phải là chính xác những gì bạn muốn? Thêm e-mail nếu người dùng là 'NormalUser', không làm gì khác. –

+0

Tôi nghĩ rằng việc thêm 'addEmail' vào lớp cơ sở là một ý tưởng hay và vô hại. Về bản chất no-op của nó, bạn có thể nghĩ về nó như là "ai đó đã nói với đối tượng Admin về một địa chỉ email, và nếu người quản trị không quan tâm, thì hãy là nó." Trường hợp nó xấu hơn là nếu bạn cần đặt 'getEmail()' trong lớp cơ sở; trong trường hợp này, việc triển khai nhất định sẽ phải ném một ngoại lệ, mà tôi thấy xấu xí. Nếu bạn làm điều này, ít nhất có một phương thức như 'supportsGetEmail()'. Tốt nhất là nếu các trang web gọi biết lớp con nào mà họ có, vì vậy bạn có thể thêm 'getEmail()' vào chỉ người dùng NormalUser. – yshavit

0

Tôi nghĩ rằng giải pháp đơn giản nhất là tạo một UserList lớp có chứa danh sách Người dùng bình thường và danh sách Quản trị viên. Một thể hiện của lớp UserList sẽ thay thế danh sách gốc.Các danh sách người dùng lớp có thể cung cấp một số phương pháp như:

  • tài getUser (chỉ số i) // thực hiện với hai danh sách

  • tài removeUser (chỉ số i) // thực hiện với hai danh sách

  • NormalUser getNormalUser (chỉ số i) // thực hiện với danh sách người dùng bình thường
  • NormalUser removeNormalUser (chỉ số i) // thực hiện với danh sách người dùng bình thường
  • quản getAdmin (chỉ số i) // thực hiện với danh sách người dùng quản trị
  • quản removeAdmin (chỉ số i) // thực hiện với danh sách người dùng quản trị
  • ....

Tất cả các mã để xử lý danh sách thích hợp sẽ được đóng gói trong lớp danh sách người dùng . Bạn có thể có các phương thức sử dụng cả hai danh sách hoặc chỉ một danh sách, tùy thuộc vào những gì bạn cần làm với người dùng. Các lớp tương tác với UserList sẽ không biết nếu chỉ có một hoặc hai danh sách bên trong Danh sách người dùng.

+0

Cảm ơn bạn đã trả lời Phil, nó có vẻ như là một ý tưởng tốt cho tôi, mặc dù tôi đã không thích phải tạo ra một lớp mới chỉ để giữ danh sách. Tôi mới ở Java nhưng nếu ví dụ tôi đã có một siêu lớp động vật, sau đó là một lớp con mèo và chó. Nếu tôi có danh sách động vật với chó và mèo, và tôi muốn gọi vỏ cây() trên chó, thì tôi sẽ phải tạo một danh sách những con chó để làm điều đó đúng không? - có vẻ như phản trực giác với tôi:/ – Brah

+0

Xin chào. Nếu tôi ở nơi bạn, tôi sẽ tạo (1) một danh sách cho mèo và (2) một danh sách cho chó. Hãy xem xét rằng bạn có 10000 con chó và 10000 con mèo. Nếu bạn tách các danh sách, nó sẽ nhanh hơn nhiều để tìm thấy tất cả những con chó, sau đó nếu bạn sẽ có một danh sách duy nhất với 20.000 động vật trộn với nhau. Khi bạn cần gọi một phương pháp như vỏ cây() bạn chỉ cần xử lý danh sách chó thay vì chế biến 20 000 động vật và phải kiểm tra xem chúng có phải là chó hay mèo trước khi gọi vỏ cây() không. Hiệu suất nên tốt hơn nhiều với hai danh sách nếu bạn có nhiều yếu tố. – Phil