2011-12-29 71 views
41

Tôi đang đọc "Mã sạch" và gặp khó khăn trong việc tìm ra cách giữ một số hàm của tôi (thường là các hàm tạo) với MAXIMUM của 3 tham số.Giảm số đối số cho một hàm tạo

Thường thì đối tượng của tôi cần rất nhiều thông tin để làm việc - tôi có nên tạo một hàm tạo nhỏ và sau đó sử dụng các hàm của trình tắt để cung cấp cho họ tất cả thông tin không? Điều này dường như không tốt hơn là chỉ sử dụng một nhà xây dựng lớn.

Ví dụ: tôi có lớp "MovablePatch". Nó cho phép người dùng kéo một hình vuông xung quanh trong một cửa sổ. Nó cần một vài tham số, bao gồm Radius, Color, Renderer, InitialPosition, và Visibility. Hiện tại tôi thu thập tất cả những thứ này từ GUI của tôi và sau đó gọi:

MovablePatch(int radius, Renderer* renderer, Color color, Position initial, bool visibility) 

Đây chỉ là một số điều tôi cần trong lớp học này. Bất cứ ai có thể đề nghị làm thế nào khác tôi có thể gói thông tin này để vượt qua để các nhà xây dựng? Tôi không thấy rõ ràng "phá vỡ nó thành những lớp nhỏ hơn" xuất hiện ở đây.

+7

Tra cứu mẫu trình tạo. – Bashwork

+1

Bạn có phải làm theo hướng dẫn đó không? Nếu không, tôi sẽ nói rất nhiều khi bỏ qua nó, nơi nó không rõ ràng làm thế nào để áp dụng nó. Imo nếu sửa đổi để làm theo một hướng dẫn không phải là trực quan nó thường không có giá trị nó để cấu trúc lại mã để đảm bảo các hướng dẫn được theo sau không có vấn đề gì. Đối với giải pháp không liên tục có thể dễ dàng dẫn đến mã phức tạp hơn (và do đó dễ bị lỗi) thậm chí không có khả năng đọc tốt hơn (ví dụ cho điều này là hàm tạo nhỏ được đề cập tiếp theo là các hàm gọi hàm mutator hoặc mô hình trình xây dựng. quên đặt giá trị). – Grizzly

+6

Sử dụng một 'cấu trúc' đơn giản cho các thuộc tính liên quan để được thông qua như một tham số đã được thực hiện kể từ trước khi tôi được sinh ra, tôi khá chắc chắn. – AJG85

Trả lời

31

Bạn có thể có

MovablePatch(Renderer* renderer, CircleAppearance circleAppearance) 

nơi CircleAppearance tập hợp các thông tin khác.

Tuy nhiên, mã sạch và các sách khác khái quát về mã tốt sẽ trông như thế nào, đang hướng tới 80% mã ở đó. Mã số của bạn dường như "gần gũi hơn với kim loại" hơn so với dòng LoB (Line of Business) điển hình. Như vậy, bạn có thể chạy vào những nơi mà các lý tưởng mã hóa nhất định không được áp dụng.

Phần quan trọng nhất là bạn đang nghĩ về nó và cố gắng giữ cho mọi thứ đẹp và ngăn nắp! :)

20

Một số thứ bạn đang truyền vào có thể được tóm tắt thành một cấu trúc lớn hơn. Ví dụ: visibility, colorradius, có thể có ý nghĩa khi được đặt vào đối tượng mà bạn xác định. Sau đó, một thể hiện của lớp này, gọi nó là ColoredCircle, có thể được truyền vào hàm tạo của MovablePatch. Một ColorCircle không quan tâm nó đang ở đâu hoặc trình renderer nào đang sử dụng, nhưng một MovablePatch thực hiện.

Điểm chính của tôi, là từ quan điểm OO, radius không thực sự là số nguyên, đó là bán kính. Bạn muốn tránh những danh sách nhà xây dựng dài này bởi vì nó là khó khăn để hiểu được bối cảnh của những điều này. Nếu bạn thu thập chúng vào một lớp học lớn hơn, giống như cách bạn đã có với ColorPosition, bạn có thể có ít tham số hơn được truyền vào và giúp bạn dễ hiểu hơn.

+2

Điều này không chỉ thay đổi vấn đề? Bạn cũng phải khởi tạo lớp 'ColorCircle' bằng cách nào đó, và vẫn còn bốn tham số còn lại ... – MartinStettner

+2

@MartinStettner, có bạn phải khởi tạo' ColoredCircle' trước, nhưng đó phải là bốn tham số. Điều đó có nghĩa là 'MovablePatch' sẽ chỉ lấy' 'ColorCircle' và' Renderer * '. –

+1

Nên 'Vị trí' nên được lưu trữ trong Bản vá hoặc trong Vòng kết nối? Tôi nghĩ miếng dán có vị trí chứ không phải hình tròn. –

8

Một tùy chọn tốt là sử dụng mẫu Builder, trong đó mỗi phương thức "setter" trả về cá thể của riêng bạn và bạn có thể kết chuỗi các phương thức mà bạn cần.

Trong trường hợp của bạn, bạn sẽ nhận được một lớp mới MovablePatchBuilder.

Cách tiếp cận này rất hữu ích và bạn có thể tìm thấy nó trong nhiều khung và ngôn ngữ khác nhau.

Tham khảo here để xem một số ví dụ.

12

Named Parameter Idiom hữu ích ở đây.Trong trường hợp của bạn, bạn có thể có

class PatchBuilder 
{ 
public: 
    PatchBuilder() { } 
    PatchBuilder& radius(int r) { _radius = r; return *this; } 
    PatchBuilder& renderer(Renderer* r) { _renderer = r; return *this; } 
    PatchBuilder& color(const Color& c) { _color = c; return *this; } 
    PatchBuilder& initial(const Position& p) { _position = p; return *this; } 
    PatchBuilder& visibility(bool v) { _visibility = v; return *this; } 

private: 
    friend class MovablePatch; 
    int _radius; 
    Renderer* _renderer; 
    Color _color; 
    Position _position; 
    bool _visibility; 
}; 

class MovablePatch 
{ 
public: 
    MovablePatch(const PatchBuilder& b) : 
     _radius(b._radius); 
     _renderer(b._renderer); 
     _color(b._color); 
     _position(b._position); 
     _visibility(b._visibility); 
    { 

    } 

private: 
    int _radius; 
    Renderer* _renderer; 
    Color _color; 
    Position _position; 
    bool _visibility; 
}; 

sau đó bạn sử dụng nó như vậy

int 
main() 
{ 
    MovablePatch foo = PatchBuilder(). 
     radius(1.3). 
     renderer(asdf). 
     color(asdf). 
     position(asdf). 
     visibility(true) 
    ; 
} 

quá đơn giản, nhưng tôi nghĩ rằng nó được những điểm trên. Nếu các thông số nhất định được yêu cầu họ có thể được đưa vào các nhà xây dựng PatchBuilder:

class PatchBuilder 
{ 
public: 
    PatchBuilder(const Foo& required) : _foo(required) { } 
    ... 
}; 

Rõ ràng mô hình này degenerates vào vấn đề ban đầu nếu tất cả các đối số được yêu cầu, trong trường hợp này tham số thành ngữ được đặt tên không được áp dụng. Vấn đề là, đây không phải là một kích thước phù hợp với tất cả các giải pháp, và như Adam mô tả trong bình luận dưới đây có chi phí bổ sung và một số chi phí với việc làm như vậy.

+1

Tôi lấy quyền tự do để thêm câu lệnh 'return * this' vào phương thức' PatchBuilder';) ... – MartinStettner

+11

Việc định hình người xây dựng thông thạo mới này là nguy hiểm. Bạn cần phải có kiểm tra thời gian chạy để xem mọi thứ đã được điền. Tôi sẽ không đề nghị này. Sự phức tạp không cần thiết sẽ tốn kém để duy trì. –

+1

@AdamDymitruk Tôi đã cập nhật câu trả lời với một số từ ngữ dựa trên đề xuất của bạn. –

28

Không lấy các cực đại như "bạn sẽ không có nhiều hơn 3 tham số trong các hàm tạo của bạn" ở giá trị khuôn mặt. Nếu bạn có cơ hội nhỏ nhất làm cho một đối tượng bất biến, hãy làm cho nó; và nếu nó không thay đổi có nghĩa là nó sẽ có một hàm tạo với 50 tham số, vì vậy hãy là nó; đi cho nó; thậm chí không nghĩ về nó hai lần.

Ngay cả khi đối tượng sẽ có thể thay đổi, bạn vẫn phải truyền hàm khởi tạo của nó khi có nhiều tham số cần thiết để ngay lập tức khi xây dựng nó sẽ ở trạng thái hợp lệ và có ý nghĩa. Trong cuốn sách của tôi, tuyệt đối không thể biết được những phương pháp đột biến ma thuật nào phải được gọi (đôi khi ngay cả theo thứ tự đúng) trước khi bất kỳ phương pháp nào khác có thể được gọi, dưới hình phạt của segfault. Điều đó đã được nói, nếu bạn thực sự muốn giảm số lượng tham số cho một hàm tạo hoặc cho bất kỳ hàm nào, chỉ cần chuyển giao phương thức này một giao diện mà nó có thể gọi để lấy nó từ thứ mà nó cần để công việc.

+7

+1 cho điều này. Tính bất biến có lợi hơn rất nhiều so với danh sách tham số nhỏ. – vocaro

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