2016-05-30 21 views
9

cuối tuần trước tôi đã đọc một số nội dung về giao diện, lớp trừu tượng và nguyên tắc thiết kế. Cuối cùng tôi đã có một chút bối rối và tôi đã cố gắng để xây dựng một ví dụ về những gì tôi đã học được (hoặc nghĩ rằng tôi đã học được).Lớp mở rộng Thực hiện lớp trừu tượng Giao diện

Đây là ví dụ của tôi: Trường hợp sẽ là mô hình hóa một lớp chứa thông tin về cây cối.

Trước hết tôi sẽ thực hiện một giao diện:

public interface Tree{ 
    public void grow(); 
} 

Giao diện nắm giữ tất cả các phương pháp đó phải được thực hiện bởi những cây bê tông. Cho đến nay rất tốt nhưng cây như vậy cần một số thuộc tính (biến) được chia sẻ trên tất cả các họ cây. Vì mục đích đó tôi sẽ sử dụng một lớp trừu tượng:

public abstract class AbstractTree implements Tree { 
    private String barColor; 
    private int maxHeight; 
    private boolean isEvergreen; 
} 

Đây có phải là cách đúng hay tôi không thể thực hiện một loại hợp đồng về thuộc tính (biến) phải ở trong các lớp khác?

Sau khi phần thuộc tính được thực hiện, tôi muốn có 3 loại cây.

  • Oak
  • Maple
  • Spruce

Vì vậy, mỗi người trong số những cây "tpyes" có thể có các biến cá nhân.

public class OakTreeImpl extends AbstractTree{ 
    private String barColor; 
    private int maxHeight; 
    private boolean isEvergreen; 
    private String foo; 
    @Override 
    public void grow() { 
    } 
} 

Liệu phương pháp này âm thanh ngay trong một thiết kế nguyên tắc cách hướng đối tượng hay am i hoàn toàn sai với nó?

+3

AbstractTree nên sử dụng trường 'được bảo vệ' để bạn không cần phải redeclare chúng trong quá trình triển khai –

+0

tại sao không chỉ sử dụng lớp trừu tượng? – VedX

+0

Tôi tuyên bố chúng như được bảo vệ trong tóm tắt và loại bỏ chúng khỏi lớp Impl? Và tôi vẫn có thể truy cập (get/set) các giá trị của các biến ngay cả khi tôi không "nhìn thấy" chúng trực tiếp trong lớp? – user2742409

Trả lời

4

Mặc dù điều này một phần có thể là chủ quan, tôi phải đồng ý với các câu trả lời khác được đưa ra cho đến nay.

Giao diện TreeNOT lỗi thời. Khi bạn muốn lập mô hình một số Tree, thì phải có giao diện Tree, nêu rõ các phương thức mà mỗi Tree đều có.

Đặc biệt, tôi khuyên bạn nên chống lại đề xuất chỉ cần thay thế bằng lớp AbstractTree. Một số người nói rằng bạn hầu như không nên sử dụng các lớp trừu tượng tại tất cả (ví dụ: Jaroslav Tulach in "Practical API Design"). Ít nhất tôi muốn nói rằng bạn nên sử dụng chúng rất một cách thận trọng. Quan trọng nhất: Bạn nên cố gắng tránh để các lớp trừu tượng xuất hiện trong giao diện công cộng của các lớp khác. Ví dụ, nếu bạn có một lớp/giao diện, với một phương pháp như

void makeGrow(Tree tree) { 
    System.out.println("Growing "+tree); 
    tree.grow(); 
} 

sau đó thay thế xuất hiện này của Tree để AbstractTree sẽ giảm tính linh hoạt. Bạn sẽ không bao giờ có thể sử dụng một lớp học mà không kế thừa từ AbstractTree - và xem xét rằng bạn chỉ có thể kế thừa từ một lớp, điều này có thể là một hạn chế nghiêm trọng. (Bạn luôn có thể triển khai nhiều giao diện - vì vậy một giao diện không giới hạn tính linh hoạt ở đây).

Nhưng ngay cả nếu bạn sử dụng lớp trừu tượng, tôi khuyên bạn nên sử dụng các trường protected một cách thận trọng. Hoặc, nói chung hơn, hãy nhận biết các tác động của kế thừa từ một lớp, như được mô tả trong mục "Mục 17 - Thiết kế và tài liệu thừa kế hoặc người khác cấm" của "Effective Java" by Joshua Bloch.

Trong nhiều trường hợp, bạn không muốn lớp kế thừa có quyền truy cập đầy đủ vào các trường. Vì vậy, ít nhất bạn nên cân nhắc tạo các trường private và chỉ cung cấp các phương thức protected cho loại quyền truy cập mà bạn muốn cấp cho các lớp kế thừa.

public interface Tree{ 
    public void grow(); 
} 

abstract class AbstractTree implements Tree { 

    // Do the values of these fields ever change? If not, 
    // then make them final, and set them only in the 
    // constructor 
    private final String barColor; 
    private final int maxHeight; 
    private final boolean evergreen; 

    protected AbstractTree(...) { ... } 

    // Subclasses are only allowed to read (but not write) these fields 
    protected final String getBarColor() { return barColor; } 
    protected final intgetMaxHeight() { return maxHeight; } 
    protected final boolean isEvergreen() { return evergreen; } 
} 
+0

triết lý thiết kế proclamed của bạn không phải là chống lại tuyên bố của tôi. Tuy nhiên, tôi tin rằng nếu bạn có một lớp trừu tượng cho cây, bạn không nên tạo ra một giao diện riêng biệt không chỉ được gọi là 'Tree', mà sẽ chỉ được sử dụng bởi lớp cây trừu tượng. Điều này không có ý nghĩa! Giao diện chỉ hữu ích, nếu bạn không biết ai sẽ thực hiện nó hoặc cần sự linh hoạt bổ sung này.Lý do duy nhất chúng tôi đang sử dụng trừu tượng ở tất cả trong trường hợp này là bởi vì bạn không thể khởi tạo một cây mới mà không xác định loại (Maple, Oak, vv) nó được. – 000000000000000000000

+1

@ 000000000000000000000 * "... nhưng sẽ chỉ được lớp cây trừu tượng sử dụng" * - Tôi nghĩ đó là vấn đề: Bạn muốn chuyển cho phương thức nào nhận cây? Bạn có muốn truyền vào 'AbstractTree' (mặc dù tên, là * specific *, bởi vì nó phải mở rộng' AbstractTree'), hay bạn muốn có thể truyền vào bất kỳ 'Tree' nào, bất kể nó được thực hiện? (I.e. bất kể việc thực hiện * này * (của 'giao diện'!) Có dựa trên' AbstractTree' hay không)? Một lần nữa, nó có thể là một phần chủ quan và cách tiếp cận của tôi có thể trông giống như "overengineering", nhưng tôi thích giao diện ở đây – Marco13

+1

(Và, bằng cách này: Tôi không thực sự hiểu tại sao bạn nghĩ rằng có thể có biện minh cho giao diện 'Plant' nhưng không phải cho giao diện 'Cây'. Đó chỉ là một câu hỏi về cái gì là" gốc "của hệ thống phân cấp khái niệm, và tôi không thấy cách này ủng hộ * chống lại * mô hình hóa' Cây' làm giao diện) – Marco13

6

Tôi muốn đánh dấu các biến mẫu là protected.

Bởi vì tất cả các thành viên được bảo vệ của một lớp siêu có thể truy cập vào các lớp con. Nếu và chỉ nếu, các lớp học phụ huynh và con đang ở trong cùng một gói

public abstract class AbstractTree implements Tree { 
    protected String barColor; 
    protected int maxHeight; 
    protected boolean isEvergreen; 
} 

public class OakTreeImpl extends AbstractTree{ 

    // I can access barColor, maxHeight, isEvergreen in this class 

    @Override 
    public void grow() { 

    } 
} 
+0

Bạn hoàn toàn đúng nhưng tôi đoán Câu trả lời của 000000000000000000000 phù hợp hơn với câu hỏi chưa được hỏi của tôi. Nhưng thx cho nỗ lực này. – user2742409

7

này làm việc, tuy nhiên nó không có ý nghĩa nhiều, vì giao diện là hoàn toàn lỗi thời trong trường hợp này.
Bạn nên thêm phương pháp nuôi để AbstractTree của bạn như thế này:

public abstract class AbstractTree{ 
    protected String barColor; 
    protected int maxHeight; 
    protected boolean isEvergreen; 

    public abstract void grow(); 
} 

Sử dụng một giao diện sẽ có ý nghĩa, nếu bạn muốn có các loại khác nhau của các nhà máy tất cả những gì sẽ có thể phát triển ví dụ.

interface Plant{ 
    void grow(); 
} 

abstract class Tree implements Plant{ 
    void grow(){ /* do sth */ } 
} 

abstract class Flower implements Plant{ 
    void grow(){ /* do sth totally different */ 
} 

Mục đích của giao diện là để cung cấp các phương pháp tương tự trong nhiều lớp học với hiện thực khác nhau, trong khi một lớp trừu tượng cung cấp phương pháp và các thuộc tính được chia sẻ trong tất cả các lớp con mình.
Nếu một phương thức trong lớp trừu tượng cũng trừu tượng, mọi lớp con phải tự thực hiện nó.

+0

Cảm ơn bạn rất nhiều, bây giờ tôi nhận được nó, hy vọng: D – user2742409

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