2010-06-28 35 views
12

Sau khi đọc cuốn sách xuất sắc nhất "Đầu mẫu thiết kế đầu tiên", tôi bắt đầu cải tiến cho đồng nghiệp của mình những lợi ích của các mẫu và nguyên tắc thiết kế. Trong khi mở rộng các nhân đức của mô hình yêu thích của tôi - Mô hình chiến lược - Tôi đã được hỏi một câu hỏi đã cho tôi tạm dừng. Tất nhiên, chiến lược sử dụng thừa kế và bố cục của tôi là "chương trình cho giao diện (hoặc siêu kiểu) không phải là triển khai", khi một đồng nghiệp hỏi "tại sao lại sử dụng lớp cơ sở trừu tượng thay vì lớp bê tông?" .
Tôi chỉ có thể nghĩ ra "bạn ép buộc các lớp con của bạn triển khai các phương thức trừu tượng và ngăn chúng khởi tạo ABC". Nhưng thành thật mà nói, câu hỏi đã khiến tôi không hiểu được. Đây có phải là những lợi ích duy nhất của việc sử dụng một lớp cơ sở trừu tượng trên một lớp cụ thể ở trên cùng của hệ thống phân cấp của tôi không?Lớp cơ sở trừu tượng so với lớp bê tông như một SuperType

+0

Theo ý kiến ​​của tôi CÓ. Nhưng tôi nghĩ đó là một "tính năng" rất quan trọng của một ngôn ngữ để ép buộc ai đó thừa hưởng từ lớp này để thực hiện một phương thức. Đôi khi bạn cần tạo một lớp chung nhưng không thể triển khai tất cả các tính năng vì nó sẽ quá cụ thể. – KroaX

Trả lời

22

Nếu bạn cần các phương pháp cụ thể được triển khai, hãy sử dụng Giao diện. Nếu có logic được chia sẻ có thể được rút ra, hãy sử dụng lớp cơ sở trừu tượng. Nếu bộ cơ sở của chức năng hoàn thành một mình, thì bạn có thể sử dụng một lớp concreate làm cơ sở. Một lớp cơ sở trừu tượng, và một Giao diện không thể được khởi tạo trực tiếp, và đó là một trong những lợi thế. Nếu bạn có thể sử dụng một loại cụ thể, thì bạn cần phải thực hiện các phương pháp ghi đè và có một "mã mùi" với nó.

+0

+1. Tuyệt vời chạy xuống của những gì để sử dụng và khi nào. – NotMe

+1

Huh? Tại sao phương pháp ghi đè mùi? – ladenedge

+5

Mã với các phương pháp ảo khó thay đổi hơn trong tương lai mà không vi phạm một số hợp đồng hành vi ngụ ý. Vì vậy, trong NET. Họ quyết định rằng làm cho một phương pháp overridable phải là một quyết định có ý thức và cần được xem xét cẩn thận. Vì vậy, bất kỳ phương pháp overridable mà không có hướng dẫn cho người thực hiện là một "mã mùi". –

1

Có, mặc dù bạn cũng có thể sử dụng giao diện để buộc một lớp học triển khai các phương pháp cụ thể.

Một lý do khác để sử dụng lớp trừu tượng trái với lớp bê tông là lớp trừu tượng rõ ràng không thể được khởi tạo. Đôi khi bạn cũng sẽ không muốn điều này xảy ra, do đó, một lớp trừu tượng là con đường để đi.

5

Chương trình giao diện, không triển khai ít liên quan đến các lớp trừu tượng và cụ thể. Ghi nhớ số template method pattern? Các lớp học, trừu tượng hoặc cụ thể, là các chi tiết triển khai.

Và lý do sử dụng các lớp trừu tượng thay vì các lớp cụ thể là bạn có thể gọi các phương thức mà không cần triển khai chúng, nhưng bằng cách để chúng được triển khai cho các lớp con thay thế.

Lập trình cho giao diện là một điều khác - nó đang xác định những gì API của bạn, không phải cách thực hiện. Và điều này được biểu thị bằng giao diện.

Lưu ý một sự khác biệt chính - bạn có thể có protected abstract phương pháp, có nghĩa là đây là chi tiết triển khai. Nhưng tất cả các phương thức giao diện đều công khai - một phần của API.

+0

Để thêm vào đó, "Chương trình thực hiện" sẽ làm những việc như sử dụng phép chọn lại để truy cập các trường riêng tư của lớp học. –

1

Trước hết, Mẫu chiến lược hầu như không bao giờ được sử dụng trong C# hiện đại. Nó chủ yếu cho các ngôn ngữ như Java không hỗ trợ các hàm con trỏ, các đại biểu, hoặc các hàm hạng nhất. Bạn sẽ thấy nó trong các phiên bản cũ của C# trong các giao diện như IComparer.

Đối với lớp cơ sở trừu tượng so với lớp bê tông, câu trả lời trong Java luôn là "Điều gì làm việc tốt hơn trong tình huống này?" Nếu chiến lược của bạn có thể chia sẻ mã, thì bằng mọi cách hãy để họ làm như vậy.

Mẫu thiết kế không phải là hướng dẫn về cách thực hiện điều gì đó. Họ là những cách để phân loại những thứ mà chúng tôi đã làm.

0

Nếu khách hàng dựa vào "hợp đồng hành vi ngụ ý [s]", nó được lập trình chống lại việc triển khai và chống lại hành vi không được bảo đảm. Việc ghi đè phương thức trong khi theo dõi hợp đồng chỉ cho thấy lỗi trong ứng dụng khách chứ không phải gây ra lỗi.

OTOH, sai lầm của các hợp đồng giả định không có ở đó ít có khả năng gây ra vấn đề nếu phương pháp được đề cập là không ảo - nghĩa là ghi đè nó không thể gây ra sự cố vì nó không thể bị ghi đè. Chỉ khi việc thực hiện phương thức ban đầu được thay đổi (trong khi vẫn tuân theo hợp đồng) thì nó có thể phá vỡ ứng dụng khách hay không.

0

Câu hỏi liệu lớp cơ sở nên trừu tượng hay cụ thể phụ thuộc phần lớn vào việc một đối tượng lớp cơ sở chỉ thực hiện các hành vi chung cho tất cả các đối tượng trong lớp sẽ hữu ích. Hãy xem xét một WaitHandle. Gọi "chờ" khi nó sẽ gây ra mã để chặn cho đến khi một số điều kiện được thỏa mãn, nhưng không có cách phổ biến để nói với một đối tượng WaitHandle rằng điều kiện của nó là hài lòng. Nếu người ta có thể khởi tạo một "WaitHandle", trái ngược với việc chỉ có thể khởi tạo các thể hiện của các kiểu có nguồn gốc, một đối tượng như vậy sẽ phải hoặc không bao giờ chờ đợi, hoặc luôn chờ đợi mãi mãi. Hành vi thứ hai sẽ khá vô dụng; trước đây có thể hữu ích, nhưng có thể đạt được gần như là tốt với một ManualResetEvent được phân bổ tĩnh (tôi nghĩ rằng sau này sẽ lãng phí một vài tài nguyên, nhưng nếu nó được phân bổ tĩnh thì tổng tổn thất tài nguyên sẽ không đáng kể). Trong nhiều trường hợp, tôi cho rằng sở thích của tôi là sử dụng các tham chiếu đến một giao diện hơn là một lớp cơ sở trừu tượng, nhưng cung cấp giao diện một lớp cơ sở cung cấp "triển khai mô hình". Vì vậy, bất kỳ nơi nào sẽ sử dụng một tham chiếu đến một MyThing, một trong những sẽ cung cấp một tham chiếu đến "iMyThing". Nó cũng có thể là 99% (hoặc thậm chí 100%) của các đối tượng iMyThing thực sự là một MyThing, nhưng nếu ai đó cần phải có một đối tượng iMyThing kế thừa từ một cái gì đó khác, người ta có thể làm như vậy.

1

Lớp cơ sở trừu tượng thường được sử dụng trong các trường hợp mà nhà thiết kế muốn bắt buộc một mẫu kiến ​​trúc trong đó các nhiệm vụ nhất định phải được thực hiện theo cùng cách thức của tất cả các lớp trong khi các hành vi khác phụ thuộc vào lớp con. dụ:

public abstract class Animal{ 

public void digest(){ 

} 

public abstract void sound(){ 

} 
} 

public class Dog extends Animal{ 
public void sound(){ 
    System.out.println("bark"); 
} 
} 

Stratergy mẫu yêu cầu nhà thiết kế để sử dụng hành vi sáng tác cho trường hợp có gia đình của alogirthms cho một hành vi.

0

thích lớp cơ sở trừu tượng trong các tình huống dưới đây:

  1. Một lớp cơ sở không thể tồn tại mà không có một tiểu class => các lớp cơ sở chỉ đơn giản là trừu tượng và nó có thể' t được khởi tạo.
  2. Lớp cơ sở không thể triển khai đầy đủ hoặc cụ thể phương thức => Triển khai phương thức là lớp cơ sở chưa hoàn chỉnh và chỉ các lớp phụ mới có thể cung cấp triển khai hoàn chỉnh.
  3. lớp cơ sở cung cấp một khuôn mẫu để thực hiện phương pháp nhưng nó vẫn phụ thuộc vào lớp bê tông để hoàn thành việc thực hiện phương pháp - Template_method_pattern

Một ví dụ đơn giản để minh họa điểm trên

Shape là trừu tượng và nó không thể tồn tại mà không có hình dạng Bê tông như Rectangle. Không thể triển khai Shape ở lớp Shape vì các hình dạng khác nhau có các công thức khác nhau.Lựa chọn tốt nhất để xử lý tình huống: rời draw() thực hiện để phân lớp

abstract class Shape{ 
    int x; 
    int y; 
    public Shape(int x,int y){ 
     this.x = x; 
     this.y = y; 
    } 
    public abstract void draw(); 
} 
class Rectangle extends Shape{ 
    public Rectangle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Rectangle using x and y : length * width 
     System.out.println("draw Rectangle with area:"+ (x * y)); 
    } 
} 
class Triangle extends Shape{ 
    public Triangle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Triangle using x and y : base * height /2 
     System.out.println("draw Triangle with area:"+ (x * y)/2); 
    } 
} 
class Circle extends Shape{ 
    public Circle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Circle using x as radius (PI * radius * radius 
     System.out.println("draw Circle with area:"+ (3.14 * x * x)); 
    } 
} 

public class AbstractBaseClass{ 
    public static void main(String args[]){ 
     Shape s = new Rectangle(5,10); 
     s.draw(); 
     s = new Circle(5,10); 
     s.draw(); 
     s = new Triangle(5,10); 
     s.draw(); 
    } 
} 

đầu ra:

draw Rectangle with area:50 
draw Circle with area:78.5 
draw Triangle with area:25 

Trên đang bao gồm điểm 1 và điểm 2. Bạn có thể thay đổi draw() phương pháp như phương pháp mẫu nếu lớp cơ sở có một số triển khai và gọi phương thức lớp con để hoàn thành chức năng draw().

Bây giờ cùng một ví dụ với mô hình phương pháp mẫu:

abstract class Shape{ 
    int x; 
    int y; 
    public Shape(int x,int y){ 
     this.x = x; 
     this.y = y; 
    } 
    public abstract void draw(); 

    // drawShape is template method 
    public void drawShape(){ 
     System.out.println("Drawing shape from Base class begins"); 
     draw(); 
     System.out.println("Drawing shape from Base class ends");  
    } 
} 
class Rectangle extends Shape{ 
    public Rectangle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Rectangle using x and y : length * width 
     System.out.println("draw Rectangle with area:"+ (x * y)); 
    } 
} 
class Triangle extends Shape{ 
    public Triangle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Triangle using x and y : base * height /2 
     System.out.println("draw Triangle with area:"+ (x * y)/2); 
    } 
} 
class Circle extends Shape{ 
    public Circle(int x,int y){ 
     super(x,y); 
    } 
    public void draw(){ 
     //Draw Circle using x as radius (PI * radius * radius 
     System.out.println("draw Circle with area:"+ (3.14 * x * x)); 
    } 
} 

public class AbstractBaseClass{ 
    public static void main(String args[]){ 
     Shape s = new Rectangle(5,10); 
     s.drawShape(); 
     s = new Circle(5,10); 
     s.drawShape(); 
     s = new Triangle(5,10); 
     s.drawShape(); 
    } 
} 

đầu ra:

Drawing shape from Base class begins 
draw Rectangle with area:50 
Drawing shape from Base class ends 
Drawing shape from Base class begins 
draw Circle with area:78.5 
Drawing shape from Base class ends 
Drawing shape from Base class begins 
draw Triangle with area:25 
Drawing shape from Base class ends 

Một khi bạn đã quyết định rằng bạn cần phải thực hiện như phương pháp abstract, bạn có hai lựa chọn: Hoặc là dùng interface hoặc abstract lớp học. Bạn có thể khai báo các phương thức của mình theo số interface và xác định lớp abstract là lớp đang triển khai interface.

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