2008-10-14 34 views
27

Tôi đang làm việc trên một dự án trong C#. Lập trình viên trước đó không biết lập trình hướng đối tượng, vì vậy hầu hết mã đều nằm trong các tệp lớn (chúng ta đang nói khoảng 4-5000 dòng) trải rộng trên hàng chục và đôi khi hàng trăm phương thức, nhưng chỉ có một lớp. Tái cấu trúc một dự án như vậy là một cam kết lớn, và vì vậy tôi đã học bán được để sống với nó bây giờ.Hiệu suất của việc sử dụng các phương pháp tĩnh vs instantiating lớp có chứa các phương thức

Bất cứ khi nào phương thức được sử dụng trong một trong các tệp mã, lớp được khởi tạo và sau đó phương thức được gọi trên cá thể đối tượng.

Tôi tự hỏi liệu có bất kỳ hình phạt hiệu suất đáng chú ý nào khi thực hiện theo cách này không? Tôi có nên làm cho tất cả các phương pháp tĩnh "cho bây giờ" và, quan trọng nhất, ứng dụng sẽ được hưởng lợi từ nó theo bất kỳ cách nào?

+0

Tôi nghĩ rằng điều này sẽ được di chuyển đến CS.SE –

Trả lời

24

Từ here, cuộc gọi tĩnh nhanh hơn từ 4 đến 5 lần so với việc tạo phiên bản mỗi khi bạn gọi phương thức thể hiện. Tuy nhiên, chúng tôi vẫn chỉ nói về hàng chục nano giây cho mỗi cuộc gọi, vì vậy bạn sẽ không nhận thấy bất kỳ lợi ích nào trừ khi bạn có vòng lặp thực sự chặt chẽ gọi phương thức hàng triệu lần và bạn có thể nhận được lợi ích tương tự bằng cách xây dựng một trường hợp bên ngoài vòng lặp đó và sử dụng lại nó.

Vì bạn phải thay đổi mọi trang web cuộc gọi để sử dụng phương pháp mới, nên có lẽ bạn nên dành thời gian cho việc tái cấu trúc dần dần.

+0

bài viết vui nhộn. Tôi chỉ mới bắt đầu làm điều này bởi vì tôi đã quan tâm đến những gì là nhanh hơn. "Trong khi object.read" hoặc For-Loop và IF. Và bài viết này chỉ là điều hoàn hảo sau khi nghiên cứu cá nhân của tôi. Rất đẹp. Bây giờ tôi có một đối tượng rất lớn được truy cập @ nhiều nơi và tôi tự hỏi thời tiết giá trị của nó truyền nó theo phương thức đến nơi nó phải đi hoặc đơn giản là tạo một lớp biến toàn cục và truy cập nó ở đó. Khó kiểm tra những gì sẽ nhanh hơn ...? :( –

2

Có vẻ ngớ ngẩn với tôi để tạo ra một đối tượng JUST để bạn có thể gọi một phương pháp mà dường như không có tác dụng phụ trên đối tượng (từ mô tả của bạn tôi giả định điều này). Dường như với tôi rằng một thỏa hiệp tốt hơn sẽ là có một số đối tượng toàn cầu và chỉ sử dụng chúng. Bằng cách đó bạn có thể đặt các biến thường sẽ là toàn cầu vào các lớp thích hợp để chúng có phạm vi nhỏ hơn một chút.

Từ đó bạn có thể từ từ di chuyển phạm vi của các đối tượng này nhỏ hơn và nhỏ hơn cho đến khi bạn có thiết kế OOP phong nha.

Sau đó, một lần nữa, cách tiếp cận I có thể sẽ sử dụng khác nhau;).

Cá nhân, tôi có khả năng sẽ tập trung vào các cấu trúc và các chức năng hoạt động trên chúng và cố gắng chuyển đổi chúng thành các lớp với các thành viên từng chút một.

Đối với khía cạnh hiệu suất của câu hỏi, phương pháp tĩnh sẽ nhanh hơn một chút (nhưng không nhiều) vì chúng không liên quan đến việc xây dựng, truyền và phá hủy một đối tượng.

3

Tôi nghĩ bạn đã trả lời một phần câu hỏi này theo cách bạn hỏi: có bất kỳ đáng chú ý nào là các hình phạt về hiệu suất trong mã mà bạn có không?

Nếu hình phạt không đáng chú ý, bạn không nhất thiết phải làm gì cả. (Mặc dù nó đi mà không nói rằng các codebase sẽ được hưởng lợi từ dramtically từ một refactor dần dần vào một mô hình OO đáng kính).

Tôi đoán những gì tôi đang nói là, một vấn đề hiệu suất chỉ là một vấn đề khi bạn nhận thấy rằng đó là một vấn đề.

7

Nó phụ thuộc vào những gì khác mà đối tượng chứa - nếu "đối tượng" chỉ là một loạt các chức năng thì nó có lẽ không phải là kết thúc của thế giới. Nhưng nếu đối tượng có chứa một loạt các đối tượng khác, sau đó instantiating nó sẽ gọi tất cả các nhà xây dựng của họ (và destructors, khi nó bị xóa) và bạn có thể nhận được phân mảnh bộ nhớ và như vậy.

Điều đó nói rằng, có vẻ như hiệu suất là vấn đề lớn nhất của bạn ngay bây giờ.

9

Tôi đã xử lý vấn đề tương tự ở nơi tôi làm việc. Các lập trình viên trước khi tôi tạo ra 1 lớp điều khiển, nơi tất cả các chức năng BLL đã được bán phá giá.

Hiện tại, chúng tôi đang thiết kế lại hệ thống và đã tạo nhiều lớp Bộ điều khiển tùy thuộc vào những gì chúng được cho là để kiểm soát ví dụ:

UserController, GeographyController, ShoppingController ...

Bên trong mỗi lớp điều khiển họ có phương pháp tĩnh mà làm cho các cuộc gọi đến bộ nhớ cache hoặc Dal sử dụng Singleton pattern.

Điều này đã cho chúng tôi 2 lợi thế chính. Nó nhanh hơn một chút (khoảng 2-3 lần nhanh hơn nhưng đã nói nanoseconds ở đây; P). Người kia là mã là rất sạch

tức

ShoppingController.ListPaymentMethods() 

thay vì

new ShoppingController().ListPaymentMethods() 

Tôi nghĩ rằng nó làm cho tinh thần để sử dụng phương pháp tĩnh hoặc các lớp học nếu lớp không duy trì bất cứ tiểu bang .

5

Bạn phải xác định mục tiêu viết lại. Nếu bạn muốn có mã OO có thể kiểm tra, có thể mở rộng, có thể mở rộng & có thể duy trì được thì bạn có thể thử sử dụng các đối tượng và các phương thức thể hiện của chúng. Sau khi tất cả điều này là lập trình hướng đối tượng, chúng tôi đang nói về ở đây, không phải lập trình hướng lớp.

Rất đơn giản đối với các đối tượng giả mạo và/hoặc giả khi bạn xác định các lớp thực hiện giao diện và bạn thực thi các phương thức mẫu. Điều này giúp kiểm tra đơn vị kỹ lưỡng nhanh chóng và hiệu quả.

Ngoài ra, nếu bạn tuân thủ các nguyên tắc OO tốt (xem SOLID tại http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29) và/hoặc sử dụng các mẫu thiết kế, bạn chắc chắn sẽ làm rất nhiều ví dụ dựa trên giao diện, và không sử dụng nhiều phương pháp tĩnh.

Đối với đề nghị này:

Có vẻ như ngớ ngẩn đến tôi để tạo ra một đối tượng JUST để bạn có thể gọi một phương thức mà dường như không có tác dụng phụ trên đối tượng (từ mô tả của bạn tôi giả định này).

Tôi thấy điều này rất nhiều trong các cửa hàng dot net và với tôi điều này vi phạm đóng gói, một khái niệm OO then chốt. Tôi không thể nói liệu một phương pháp có tác dụng phụ hay không là phương pháp là tĩnh hay không. Cũng như phá vỡ đóng gói này có nghĩa là bạn sẽ cần phải thay đổi phương pháp từ tĩnh để dụ nếu/khi bạn sửa đổi chúng để có tác dụng phụ. Tôi đề nghị bạn đọc nguyên tắc Mở/Đóng cho nguyên tắc này và xem cách tiếp cận được gợi ý, được trích dẫn ở trên, hoạt động như thế nào trong đầu.

Hãy nhớ rằng hạt dẻ cũ, 'tối ưu hóa sớm là gốc rễ của mọi điều ác'. Tôi nghĩ trong trường hợp này, điều này có nghĩa là không nhảy qua hoops bằng cách sử dụng các kỹ thuật không thích hợp (tức là lập trình hướng lớp) cho đến khi bạn biết bạn có vấn đề về hiệu suất. Ngay cả sau đó gỡ lỗi vấn đề và tìm kiếm phù hợp nhất.

2

Phương pháp tĩnh nhanh hơn rất nhiều và sử dụng ít bộ nhớ hơn nhiều. Có quan niệm sai lầm rằng nó chỉ nhanh hơn một chút. Đó là một chút nhanh hơn miễn là bạn không đặt nó trên các vòng. BTW, một số vòng trông nhỏ nhưng thực sự không phải vì cuộc gọi phương thức có chứa vòng lặp cũng là một vòng lặp khác. Bạn có thể cho biết sự khác biệt trong mã thực hiện chức năng hiển thị. Rất ít bộ nhớ không may là đúng trong nhiều trường hợp. Một ví dụ cho phép dễ dàng chia sẻ thông tin với các phương thức chị em. Một phương pháp tĩnh sẽ yêu cầu thông tin khi anh ta cần.

Nhưng cũng giống như trong việc lái ô tô, tốc độ mang lại trách nhiệm. Các phương thức tĩnh thường có nhiều tham số hơn so với các đối tác thể hiện của chúng. Bởi vì một cá thể sẽ xử lý các biến được chia sẻ trong bộ nhớ đệm, các phương thức cá thể của bạn sẽ trông đẹp hơn.

ShapeUtils.DrawCircle(stroke, pen, origin, radius); 

ShapeUtils.DrawSquare(stroke, pen, x, y, width, length); 

VS

ShapeUtils utils = new ShapeUtils(stroke,pen); 

util.DrawCircle(origin,radius); 

util.DrawSquare(x,y,width,length); 

Trong trường hợp này, bất cứ khi nào các biến được sử dụng bởi tất cả các phương pháp hầu hết thời gian, phương pháp dụ là khá giá trị nó. Các trường hợp KHÔNG GIỚI THIỆU NHÀ NƯỚC, nó là về CHIA SẺ mặc dù NHÀ NƯỚC COMMON là một dạng tự nhiên CHIA SẺ, họ KHÔNG PHẢI LÀ CÙNG. Quy tắc chung của ngón tay cái là: nếu phương pháp được kết hợp chặt chẽ với các phương pháp khác --- chúng yêu nhau rất nhiều khi chúng được gọi, người kia cần được gọi và chúng có thể chia sẻ cùng một cốc nước-- -, nó nên được làm ví dụ. Để dịch các phương thức tĩnh thành các phương thức instance thì không khó. Bạn chỉ cần lấy các tham số được chia sẻ và đặt chúng dưới dạng các biến mẫu. Cách khác xung quanh là khó hơn.

Hoặc bạn có thể tạo lớp proxy sẽ kết nối các phương pháp tĩnh. Trong khi nó có vẻ là không hiệu quả hơn trong lý thuyết, thực hành cho một câu chuyện khác nhau. Điều này là bởi vì bất cứ khi nào bạn cần gọi một DrawSquare một lần (hoặc trong một vòng lặp), bạn đi thẳng đến phương thức tĩnh. Nhưng bất cứ khi nào bạn sẽ sử dụng nó hơn và hơn cùng với DrawCircle, bạn sẽ sử dụng proxy cá thể. Một ví dụ là các lớp System.IO FileInfo (instance) vs File (tĩnh).

Phương pháp tĩnh có thể kiểm tra. Trong thực tế, thậm chí nhiều testable hơn ví dụ một lần. Phương pháp GetSum (x, y) sẽ rất có thể kiểm tra để không chỉ kiểm tra đơn vị mà còn kiểm tra tải, thử nghiệm tích hợp và kiểm tra sử dụng. Phương pháp cá thể là tốt cho các bài kiểm tra đơn vị nhưng khủng khiếp cho mọi bài kiểm tra khác (mà quan trọng hơn các bài kiểm tra đơn vị BTW) đó là lý do tại sao chúng tôi nhận được rất nhiều lỗi trong những ngày này. Điều làm cho ALL Methods untestable là các tham số không có ý nghĩa như (Sender s, EventArgs e) hoặc trạng thái toàn cục như DateTime.Now. Trong thực tế, các phương thức tĩnh rất tốt ở khả năng kiểm thử mà bạn thấy ít lỗi trong mã C của một bản phân phối Linux mới hơn so với trình lập trình OO trung bình của bạn (anh ta có đầy đủ các lỗi mà tôi biết).

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