2012-09-04 29 views
6

Vì vậy, tôi quyết định sử dụng Mẫu thiết kế nhà máy cùng với Injection phụ thuộc.Quá nhiều cấu trúc đối tượng cho khuôn mẫu phụ thuộc/Mô hình kế thừa kế thừa

class ClassA 
{   
    Object *a, *b, *c; 
    public: 
    ClassA(Object *a, Object *b, Object *c) : 
    a(a), b(b), c(c) {} 
}; 

class ClassB : public ClassA 
{   
    Object *d, *e, *f; 
    public: 
    ClassB(Object *a, Object *b, Object *c, Object *d, Object *e, Object *f) : 
    ClassA(a, b, c), d(d), e(e), f(f) {} 
}; 

Bây giờ, vấn đề là lớpB có quá nhiều đối số cho hàm tạo. Đây là một ví dụ lớp thừa kế đơn, nhưng khi các lớp thừa kế bắt đầu nhận sâu hơn, và khi mỗi lớp lớp cần nhiều đối tượng được xây dựng, hàm tạo trong lớp trên cùng kết thúc đòi hỏi quá nhiều đối số để được tạo!

Tôi biết tôi có thể sử dụng setters thay vì constructor, nhưng có cách nào khác không?

+12

Cách tiếp cận chung của tôi là tránh thừa kế càng nhiều càng tốt, và cố gắng giữ cho mỗi lớp tập trung vào một trách nhiệm duy nhất. Bạn có thể xây dựng 'ClassA' một cách riêng biệt, và sau đó khởi tạo' ClassB' với một tham chiếu đến đó, chứ không phải là kết nối chặt chẽ chúng thông qua kế thừa? –

+2

Mike Seymour là đúng. Ưu tiên mối quan hệ (thành phần) có mối quan hệ chặt chẽ hơn là một mối quan hệ (thừa kế). Có lẽ nếu bạn giải thích những gì bạn muốn làm, chúng tôi có thể cho bạn biết làm thế nào tốt hơn để thực hiện nó? – metal

+2

Tôi không thấy bất kỳ vấn đề lớn với thừa kế. Nó nên được sử dụng ở mọi nơi cần thiết. Nó không nên lạm dụng như mọi thứ khác. –

Trả lời

1

Đây là một trong những vấn đề C++ (nếu điều này có thể được gọi là sự cố). Nó không có giải pháp khác ngoài việc cố gắng giữ số lượng tham số của ctor tối thiểu.

Một trong những phương pháp được sử dụng đạo cụ struct thích:

struct PropsA 
{ 
    Object *a, *b, *c; 
}; 

class ClassA 
{ 
    ClassA(PropsA &props, ... other params); 
}; 

Điều này có vẻ hiển nhiên, nhưng tôi đã sử dụng điều này nhiều lần. Trong nhiều trường hợp nó chỉ ra rằng một số nhóm params có liên quan. Trong trường hợp này, bạn nên xác định cấu trúc cho chúng.

Cơn ác mộng tồi tệ nhất của tôi về loại này là với các lớp bọc mỏng. Phương thức và trường dữ liệu của cơ sở có thể được truy cập trực tiếp trong khi tất cả các ctors phải được nhân đôi. Khi có hơn 10 ctors, việc tạo ra một wrapper bắt đầu là một vấn đề.

+0

Trong hầu hết các trường hợp, sẽ có nghĩa là làm cho 'PropsA' trở thành một lớp học chính thức bằng cách đẩy các phương thức liên quan vào nó. Nếu không, bạn chỉ cần kết thúc với một cấu trúc dữ liệu mà không phải là rất hướng đối tượng. – casablanca

+1

Tôi đặt các yêu cầu của ứng dụng, tốc độ phát triển và sự rõ ràng của mã FAR FAR trước các nội dung ưa thích là "hướng đối tượng". –

+1

OOP không phải là "ưa thích", nhưng đó là một lựa chọn mà bạn đi hoặc rời khỏi. Tôi ổn với một người chỉ làm theo cách thủ tục, sử dụng cấu trúc và chức năng, nhưng nếu bạn đang lên kế hoạch sử dụng các lớp, nó trả tiền để nỗ lực làm đúng, nếu không bạn sẽ gặp phải một mớ hỗn độn hướng đối tượng và phần còn lại là thủ tục - tôi không nghĩ điều đó dẫn đến sự rõ ràng nhiều. – casablanca

6

Setter không được khuyến nghị cho những thứ như vậy vì chúng dẫn đến một vật được xây dựng một phần vốn rất dễ bị lỗi. Một mẫu chung để xây dựng một đối tượng yêu cầu nhiều tham số là sử dụng trình tạo. Trách nhiệm của ClassBBuilder là tạo ra các đối tượng ClassB. Bạn làm cho hàm tạo của ClassB trở nên riêng tư và chỉ cho phép người xây dựng gọi nó bằng cách sử dụng mối quan hệ bạn bè. Bây giờ, những người xây dựng có thể nhìn bằng cách nào đó như thế này

ClassBBuilder { 
    public: 
    ClassBBuilder& setPhoneNumber(const string&); 
    ClassBBuilder& setName(consg string&); 
    ClassBBuilder& setSurname(const string&); 
    ClassB* build(); 
} 

Và bạn sử dụng builder thích này:

ClassB* b = ClassBBuilder().setName('alice').setSurname('Smith').build(); 

xây dựng() kiểm tra phương pháp mà tất cả các thông số cần thiết đã được thiết lập và nó hoặc lợi nhuận được xây dựng đúng đối tượng hoặc VÔ GIÁ TRỊ. Không thể tạo đối tượng được xây dựng một phần. Bạn vẫn có một hàm tạo với nhiều đối số, nhưng nó là riêng tư và chỉ được gọi ở một nơi duy nhất. Khách hàng sẽ không nhìn thấy nó. Các phương thức Builder cũng độc đáo ghi lại ý nghĩa của mỗi tham số (khi bạn thấy ClassB ('foo', 'bar'), bạn cần kiểm tra hàm tạo để tìm ra tham số nào là tên và họ là gì).

+0

Điều này có thể đẹp hơn một chút so với những người định cư, nhưng bạn đang trì hoãn hiệu quả kiểm tra thông tin đầy đủ có mặt để xây dựng một đối tượng để chạy. Tôi không nghĩ rằng một sự cải tiến đối với một nhà xây dựng đơn giản, lấy một vài lý lẽ. –

+0

Nếu bạn muốn thực thi biên dịch thời gian, đôi khi bạn vẫn có thể hưởng lợi từ người xây dựng. Đây là trường hợp khi một hàm tạo lớp lấy nhiều tham số tùy chọn với các giá trị mặc định. Sau đó, bạn có thể sử dụng trình cài đặt của trình xây dựng để đặt các tham số tùy chọn này và có phương thức build() lấy tất cả các tham số bắt buộc. Điều này sẽ cung cấp cho bạn thời gian biên dịch mà tất cả các thông số cần thiết được thông qua nhưng sẽ dễ đọc hơn và ít bị lỗi hơn lệnh gọi hàm tạo cần nhiều tham số, một số yêu cầu một số tùy chọn với giá trị mặc định. –

+1

Tôi thích cách tiếp cận này. Mã cho một khách hàng xây dựng đối tượng là sạch hơn nhiều. Mặc dù đúng là bạn mất kiểm tra thời gian biên dịch, hàm build() vẫn cho phép kiểm tra xác thực trong mã của bạn. –

1

Tôi nghĩ rằng những gì bạn mô tả không phải là một vấn đề trong C++ - trên thực tế, C++ phản ánh sự phụ thuộc thể hiện qua thiết kế của bạn khá tốt:

  1. Để xây dựng một đối tượng kiểu ClassA, bạn cần phải có ba Object trường hợp (a, bc).
  2. Để xây dựng một đối tượng thuộc loại ClassB, bạn cũng cần phải có ba Object trường hợp (d, ef).
  3. Mọi đối tượng thuộc loại ClassB có thể được coi là đối tượng thuộc loại ClassA.

Điều này có nghĩa rằng để xây dựng một đối tượng kiểu ClassB bạn cần cung cấp ba Object đối tượng đó là cần thiết cho việc thực hiện của giao diện ClassA, và sau đó ba khác để thực hiện các giao diện ClassB.

Tôi tin rằng sự cố thực tế đây là thiết kế của bạn. Bạn có thể xem xét các cách tiếp cận khác nhau để giải quyết vấn đề này:

  1. Đừng để ClassB kế thừa ClassA. Có thể hoặc không thể là tùy chọn tùy thuộc vào việc bạn có cần truy cập đồng nhất vào các đối tượng thuộc loại nào hay không (giả sử bạn có một bộ sưu tập là ClassA* và bộ sưu tập này cũng có thể chứa các con trỏ tới ClassB).
  2. Tìm các đối tượng luôn xuất hiện cùng nhau. Giống như - có thể hai đối tượng đầu tiên được truyền cho một trong hai hàm tạo (ab hoặc de) đại diện cho một số loại cặp. Có thể một định danh đối tượng hoặc tương tự? Trong trường hợp này, có thể có ích khi giới thiệu một abstract (đọc: type) dành riêng cho việc này.