Sẽ có ý nghĩa khi sử dụng các đối tượng (thay vì các kiểu nguyên thủy) cho mọi thứ trong C ++?


17

Trong một dự án gần đây tôi đang thực hiện, tôi đã phải sử dụng rất nhiều chức năng giống như thế này:

static bool getGPS(double plane_latitude, double plane_longitude, double plane_altitude,
                       double plane_roll, double plane_pitch, double plane_heading,
                       double gimbal_roll, double gimbal_pitch, double gimbal_yaw, 
                       int target_x, int target_y, double zoom,
                       int image_width_pixels, int image_height_pixels,
                       double & Target_Latitude, double & Target_Longitude, double & Target_Height);

Vì vậy, tôi muốn cấu trúc lại nó để trông giống như thế này:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                            PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

Điều này đối với tôi có thể dễ đọc và an toàn hơn đáng kể so với phương pháp đầu tiên. Nhưng nó có ý nghĩa để tạo PixelCoordinatePixelSizecác lớp? Hoặc tôi sẽ tốt hơn nếu chỉ sử dụng std::pair<int,int>cho mỗi. Và nó có ý nghĩa để có một ZoomLevellớp học, hay tôi chỉ nên sử dụng một double?

Trực giác của tôi đằng sau việc sử dụng các lớp học cho mọi thứ dựa trên những giả định sau:

  1. Nếu có các lớp cho tất cả mọi thứ, sẽ không thể vượt qua một ZoomLevelnơi mà một Weightđối tượng được mong đợi, do đó việc cung cấp các đối số sai cho một hàm sẽ khó khăn hơn
  2. Tương tự, một số hoạt động bất hợp pháp sẽ gây ra lỗi biên dịch, chẳng hạn như thêm GPSCoordinatemột ZoomLevelhoặc một cái khácGPSCoordinate
  3. Hoạt động pháp lý sẽ dễ dàng để đại diện và loại an toàn. tức là trừ hai GPSCoordinates sẽ mang lại mộtGPSDisplacement

Tuy nhiên, hầu hết mã C ++ mà tôi thấy sử dụng rất nhiều kiểu nguyên thủy và tôi tưởng tượng rằng phải có một lý do chính đáng cho điều đó. Đó có phải là một ý tưởng tốt để sử dụng các đối tượng cho bất cứ điều gì, hoặc nó có nhược điểm mà tôi không nhận thức được?


8
"Hầu hết mã C ++ mà tôi thấy sử dụng rất nhiều kiểu nguyên thủy" - hai ý nghĩ xuất hiện: rất nhiều mã C ++ có thể được viết bởi các lập trình viên quen thuộc với C có độ trừu tượng yếu hơn; cũng là Luật của Sturgeon
congusbongus 28/03/13

3
Và tôi nghĩ đó là bởi vì các kiểu nguyên thủy là đơn giản, đơn giản, nhanh chóng, kiểu dáng đẹp và hiệu quả. Bây giờ trong ví dụ của OP, chắc chắn nên giới thiệu một số lớp mới. Nhưng câu trả lời cho câu hỏi tiêu đề (trong đó nó nói "tất cả mọi thứ") là một tiếng vang không!
Ông Lister

1
@Max đã gõ hai lần hoàn toàn không có gì để giải quyết vấn đề của OP.
Ông Lister

1
@CongXu: Tôi cũng có suy nghĩ gần như tương tự, nhưng theo nghĩa nhiều hơn "rất nhiều mã trong bất kỳ ngôn ngữ nào có thể được viết bởi các lập trình viên có vấn đề với việc xây dựng trừu tượng hóa".
Doc Brown

1
Như câu nói "nếu bạn có một hàm với 17 tham số, có lẽ bạn đã bỏ lỡ một vài".
Joris Timmermans

Câu trả lời:


24

Vâng chắc chắn. Các hàm / phương thức có quá nhiều đối số là một mùi mã và chỉ ra ít nhất một trong các cách sau:

  1. Hàm / phương thức đang làm quá nhiều thứ cùng một lúc
  2. Hàm / phương thức yêu cầu quyền truy cập vào nhiều thứ vì nó yêu cầu, không nói hoặc vi phạm một số luật thiết kế OO
  3. Các đối số thực sự có liên quan chặt chẽ

Nếu trường hợp cuối cùng là trường hợp (và ví dụ của bạn chắc chắn gợi ý như vậy), thì đã đến lúc bạn nên thực hiện một số "trừu tượng" về chiếc quần lạ mắt mà những đứa trẻ tuyệt vời đã nói về oh, chỉ vài thập kỷ trước. Trên thực tế, tôi sẽ đi xa hơn ví dụ của bạn và làm một cái gì đó như:

static GPSCoordinate getGPS(Plane plane, Angle3D gimbalAngle, GPSView view)

(Tôi không quen thuộc với GPS nên có lẽ đây không phải là một sự trừu tượng chính xác; ví dụ, tôi không hiểu zoom phải làm gì với một chức năng được gọi getGPS.)

Vui lòng thích các lớp như PixelCoordinatehơn std::pair<T, T>, ngay cả khi cả hai có cùng dữ liệu. Nó thêm giá trị ngữ nghĩa, cộng với một chút an toàn loại thêm (trình biên dịch sẽ ngăn bạn chuyển từ a ScreenCoordinateđến PixelCoordinatemặc dù cả hai đều là pairs.


1
không đề cập đến việc bạn có thể vượt qua các cấu trúc lớn hơn bằng cách tham khảo cho một chút tối ưu hóa vi mô mà tất cả những đứa trẻ tuyệt vời đang cố gắng thực hiện nhưng thực sự không nên
ratchet freak

6

Tuyên bố miễn trừ trách nhiệm: Tôi thích C hơn C ++

Thay vì các lớp học, tôi sẽ sử dụng cấu trúc. Điểm này là mô phạm tốt nhất, vì cấu trúc và các lớp gần giống nhau (xem ở đây cho một biểu đồ đơn giản, hoặc câu hỏi SO này ), nhưng tôi nghĩ rằng có sự khác biệt trong sự khác biệt.

Các lập trình viên C quen thuộc với các cấu trúc như các khối dữ liệu có ý nghĩa lớn hơn khi được nhóm lại, trong khi khi tôi nhìn thấy một clasas, tôi giả sử có một số trạng thái bên trong và phương thức để thao tác trạng thái đó. Một tọa độ GPS không có trạng thái, chỉ có dữ liệu.

Từ câu hỏi của bạn, có vẻ như đây chính xác là những gì bạn đang tìm kiếm, một thùng chứa chung, không phải là một cấu trúc nhà nước. Như những người khác đã đề cập, tôi sẽ không bận tâm đến việc thu phóng vào một lớp của riêng mình; Cá nhân tôi ghét các lớp được xây dựng để giữ một kiểu dữ liệu (các giáo sư của tôi thích tạo HeightIdcác lớp).

Nếu có các lớp cho tất cả mọi thứ, sẽ không thể vượt qua ZoomLevel trong đó đối tượng Trọng lượng được mong đợi, do đó sẽ khó khăn hơn khi cung cấp các đối số sai cho hàm

Tôi không nghĩ đây là một vấn đề. Nếu bạn giảm số lượng tham số bằng cách nhóm các điều rõ ràng như bạn đã thực hiện, các tiêu đề sẽ đủ để ngăn chặn các vấn đề đó. Nếu bạn thực sự lo lắng, hãy tách riêng các nguyên hàm với một cấu trúc, nó sẽ cung cấp cho bạn các lỗi biên dịch cho các lỗi phổ biến. Thật dễ dàng để trao đổi hai tham số cạnh nhau, nhưng khó hơn nếu chúng cách xa nhau.

Tương tự, một số hoạt động bất hợp pháp sẽ gây ra lỗi biên dịch, chẳng hạn như thêm GPSCoordine vào ZoomLevel hoặc GPSCoordine khác

Sử dụng tốt quá tải nhà điều hành.

Hoạt động pháp lý sẽ dễ dàng để đại diện và loại an toàn. tức là trừ hai GPSCoord sẽ mang lại vị trí GPS

Cũng sử dụng tốt quá tải nhà điều hành.

Tôi nghĩ rằng bạn đang đi đúng hướng. Một điều khác cần xem xét là làm thế nào điều này phù hợp với phần còn lại của chương trình.


3

Sử dụng các loại chuyên dụng có lợi thế là khó khăn hơn nhiều để sắp xếp thứ tự các đối số. Ví dụ, phiên bản đầu tiên của chức năng ví dụ của bạn bắt đầu bằng một chuỗi 9 nhân đôi. Khi ai đó sử dụng chức năng này, rất dễ dàng để điều chỉnh thứ tự và vượt qua các góc gimbal thay cho tọa độ GPS và ngược lại. Điều này có thể dẫn đến các lỗi rất lạ và khó theo dõi. Thay vào đó, khi bạn chỉ truyền ba đối số với các loại khác nhau, lỗi này có thể bị trình biên dịch bắt lỗi.

Thực tế tôi sẽ xem xét để tiến thêm một bước và giới thiệu các loại giống hệt nhau về mặt kỹ thuật nhưng có ý nghĩa ngữ nghĩa khác nhau. Ví dụ: bằng cách có một lớp PlaneAngle3dvà một lớp riêng biệt GimbalAngle3d, mặc dù cả hai đều là tập hợp của ba nhân đôi. Điều này sẽ làm cho không thể sử dụng cái này như cái kia, trừ khi nó có chủ ý.

Tôi khuyên bạn nên sử dụng typedefs chỉ là bí danh cho các kiểu nguyên thủy ( typedef double ZoomLevel) không chỉ vì lý do đó mà còn cho một lý do khác: Nó dễ dàng cho phép bạn thay đổi loại sau này. Khi bạn có ý tưởng một ngày nào đó rằng việc sử dụng floatcó thể tốt như doublevậy nhưng có thể hiệu quả hơn về bộ nhớ và CPU, bạn chỉ cần thay đổi điều này ở một nơi chứ không phải dozends.


+1 cho đề xuất typedef. Giúp bạn trở thành người giỏi nhất của cả hai thế giới: các kiểu và đối tượng nguyên thủy.
Karl Bielefeldt

Không khó để thay đổi kiểu thành viên của lớp hơn là typedef; hoặc bạn có nghĩa là so với người nguyên thủy?
MaHuJa

2

Câu trả lời tốt ở đây đã có, nhưng có một điều tôi muốn thêm:

Sử dụng PixelCoordinatePixelSizelớp học làm cho mọi thứ rất cụ thể. Một chung Coordinatehoặc Point2Dlớp có thể phù hợp hơn với nhu cầu của bạn. A Point2Dphải là một lớp thực hiện các hoạt động điểm / vectơ phổ biến nhất. Bạn có thể thực hiện một lớp như vậy thậm chí một cách khái quát ( Point2D<T>trong đó T có thể inthoặc doublehoặc một cái gì đó khác).

Các hoạt động điểm / vectơ 2D rất phổ biến đối với nhiều tác vụ lập trình hình học 2D mà theo kinh nghiệm của tôi, một quyết định như vậy sẽ tăng cơ hội sử dụng lại các hoạt động 2D hiện có. Bạn sẽ tránh được sự cần thiết của tái thực hiện chúng cho mỗi PixelCoordinate, GPSCoordinate"whatsover" Phối lớp một lần nữa và một lần nữa.


1
bạn cũng có thể làm struct PixelCoordinate:Point2D<int>nếu bạn muốn loại an toàn phù hợp
ratchet freak

0

Vì vậy, tôi muốn cấu trúc lại nó để trông giống như thế này:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                        PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

Điều này đối với tôi có thể dễ đọc và an toàn hơn đáng kể so với phương pháp đầu tiên.

Nó là [dễ đọc hơn]. Đó cũng là một ý tưởng tốt.

Nhưng nó có ý nghĩa để tạo các lớp PixelCoordine và PixelSize không?

Nếu chúng đại diện cho các khái niệm khác nhau, nó có ý nghĩa (đó là "có.").

Hoặc tôi sẽ tốt hơn nếu chỉ sử dụng std :: cặp cho mỗi.

Bạn có thể bắt đầu bằng cách làm typedef std::pair<int,int> PixelCoordinate, PixelSize;. Bằng cách này, bạn bao quát ngữ nghĩa (bạn đang làm việc với tọa độ pixel và kích thước pixel, không phải với cặp std :: trong mã máy khách của bạn) và nếu bạn cần gia hạn, bạn chỉ cần thay thế typedef bằng một lớp và mã máy khách của bạn sẽ yêu cầu thay đổi tối thiểu.

Và nó có ý nghĩa khi có một lớp ZoomLevel, hay tôi chỉ nên sử dụng gấp đôi?

Bạn cũng có thể gõ nó, nhưng tôi sẽ sử dụng doublecho đến khi tôi cần chức năng liên quan đến chức năng không tồn tại gấp đôi (ví dụ: có một ZoomLevel::defaultgiá trị sẽ yêu cầu bạn xác định một lớp, nhưng cho đến khi bạn có bất cứ thứ gì như đó, giữ cho nó một đôi).

Trực giác của tôi đằng sau việc sử dụng các lớp học cho mọi thứ dựa trên những giả định này [bỏ qua cho ngắn gọn]

Chúng là những đối số mạnh mẽ (bạn nên luôn cố gắng làm cho giao diện của bạn dễ sử dụng một cách chính xác và khó sử dụng không chính xác).

Tuy nhiên, hầu hết mã C ++ mà tôi thấy sử dụng rất nhiều kiểu nguyên thủy và tôi tưởng tượng rằng phải có một lý do chính đáng cho điều đó.

Có lý do; trong rất nhiều trường hợp, những lý do đó không tốt Một lý do hợp lệ để làm điều này có thể là hiệu suất, nhưng điều đó có nghĩa là bạn nên xem loại mã này là một ngoại lệ, không phải là một quy tắc.

Đó có phải là một ý tưởng tốt để sử dụng các đối tượng cho bất cứ điều gì, hoặc nó có nhược điểm mà tôi không nhận thức được?

Nói chung, nên đảm bảo rằng nếu các giá trị của bạn luôn xuất hiện cùng nhau (và không có ý nghĩa gì), bạn nên nhóm chúng lại với nhau (vì những lý do tương tự bạn đã đề cập trong bài đăng của mình).

Đó là, nếu bạn có tọa độ luôn có x, y và z, thì tốt hơn là có một coordinatelớp hơn là truyền ba tham số ở mọi nơi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.