it-swarm-vi.com

Ưu điểm của lập trình hướng đối tượng

Lưu ý : câu hỏi này là một đoạn trích được chỉnh sửa từ một bài đăng trên blog Tôi đã viết vài tháng trước. Sau khi đặt một liên kết đến blog trong bình luận trên Lập trình viên. Có ai đó đã yêu cầu tôi đăng câu hỏi ở đây để họ có thể trả lời. Bài đăng này là phổ biến nhất của tôi, vì mọi người dường như gõ "Tôi không nhận được lập trình hướng đối tượng" vào Google rất nhiều. Hãy trả lời ở đây, hoặc trong một bình luận tại Wordpress.

Lập trình hướng đối tượng là gì? Không ai cho tôi một câu trả lời thỏa đáng. Tôi cảm thấy như bạn sẽ không nhận được một định nghĩa tốt từ một người nào đó xung quanh nói rằng đối tượng của Google và đối tượng hướng vào đối tượng với mũi của anh ấy trong không khí. Bạn cũng sẽ không nhận được một định nghĩa tốt từ một người không làm gì ngoài lập trình hướng đối tượng. Không ai hiểu cả lập trình hướng đối tượng và lập trình đã từng cho tôi một ý tưởng nhất quán về những gì một chương trình hướng đối tượng thực sự làm.

Ai đó có thể xin vui lòng cho tôi ý tưởng của họ về những lợi thế của lập trình hướng đối tượng?

35
Joel J. Adamson

Hãy nghĩ về phần mềm như một máy hoặc dòng hội tồn tại bên trong máy tính. Một số nguyên liệu thô và các thành phần được đưa vào máy, và nó tuân theo một bộ quy trình để xử lý chúng thành một số sản phẩm cuối cùng. Các quy trình được thiết lập để thực hiện một thao tác cụ thể trên một số nguyên liệu thô hoặc thành phần cho một bộ thông số cụ thể (ví dụ: thời gian, nhiệt độ, khoảng cách, v.v.) theo một thứ tự cụ thể. Nếu chi tiết của hoạt động được thực hiện không chính xác hoặc cảm biến của máy không được hiệu chuẩn chính xác hoặc nếu một số nguyên liệu thô hoặc thành phần không nằm trong tiêu chuẩn chất lượng dự kiến, nó có thể thay đổi kết quả của hoạt động và sản phẩm sẽ không thành công như mong đợi.

Một máy như vậy là rất cứng nhắc trong hoạt động của nó và đầu vào chấp nhận được. Máy móc không đặt câu hỏi về trí thông minh của nhà thiết kế cũng như môi trường hoạt động hiện tại của nó. Nó sẽ tiếp tục làm theo các thủ tục miễn là nó được hướng dẫn. Ngay cả khi một sự thay đổi trong nguyên liệu thô hoặc các thành phần có thể có tác động lớn đến những gì xảy ra trong các hoạt động sau này, máy vẫn sẽ thực hiện các quy trình của mình. Quá trình sẽ cần được xem xét để xem những thay đổi nào đối với các thủ tục là cần thiết để bù đắp và tạo ra kết quả mong muốn. Thay đổi về thiết kế hoặc cấu hình của sản phẩm cũng có thể yêu cầu thay đổi đáng kể đối với các hoạt động được thực hiện hoặc đơn đặt hàng của chúng. Mặc dù những người phụ trách sản xuất đã nhanh chóng biết được tầm quan trọng của việc cô lập các hoạt động càng nhiều càng tốt để giảm các tác động không mong muốn giữa chúng, rất nhiều giả định được đưa ra từ các thành phần điều kiện khi chúng trải qua quá trình xử lý; các giả định có thể không được phát hiện cho đến khi sản phẩm cuối cùng nằm trong tay người dùng trong một số môi trường hoạt động khác nhau.

Đó là những gì lập trình thủ tục là như thế.

Những gì hướng đối tượng cung cấp là một cách để loại bỏ các giả định về tình trạng của các thành phần; do đó, các hoạt động được thực hiện trên thành phần đó và cách tích hợp nó vào sản phẩm cuối cùng. Nói cách khác, OOP giống như lấy chi tiết quy trình để xử lý một số thành phần cụ thể và đưa nó cho một máy nhỏ hơn để làm. Máy lớn hơn chịu trách nhiệm cho quy trình nói với máy cụ thể thành phần hoạt động nó dự kiến ​​sẽ được thực hiện nhưng để lại các chi tiết cho các bước để máy cụ thể thành phần xử lý.

Đối với những lợi thế của hướng đối tượng so với phần mềm không hướng đối tượng:

  • hành vi dành riêng cho thành phần - cung cấp chi tiết về cách xử lý một thành phần cụ thể, trách nhiệm của máy cụ thể thành phần nhỏ hơn đảm bảo bất kỳ lúc nào thành phần đó được xử lý, máy của nó sẽ thực hiện một cách thích hợp;
  • biểu thức đa hình - vì các máy cụ thể thành phần thực hiện các hoạt động được điều chỉnh theo thành phần cụ thể của nó, cùng một thông điệp được gửi đến các máy khác nhau có thể hoạt động khác nhau;
  • loại trừu tượng - thường có ý nghĩa đối với một số loại thành phần khác nhau sử dụng cùng một từ vựng cho các hoạt động mà máy của chúng thực hiện;
  • tách mối quan tâm - để lại chi tiết cụ thể cho thành phần cho máy của họ có nghĩa là máy xử lý chỉ cần xử lý các mối quan tâm chung hơn, lớn hơn của quy trình và dữ liệu cần thiết để quản lý nó; cộng với, nó ít có khả năng bị ảnh hưởng bởi những thay đổi trong các thành phần khác;
  • khả năng thích ứng - các thành phần tập trung vào lĩnh vực chuyên môn của chúng có thể được điều chỉnh phù hợp với việc sử dụng không lường trước chỉ bằng cách thay đổi các thành phần mà nó sử dụng hoặc cung cấp cho máy xử lý khác;
  • tái sử dụng mã - các thành phần có trọng tâm hẹp và khả năng thích ứng lớn hơn có thể tận dụng chi phí phát triển của chúng bằng cách đưa vào sử dụng thường xuyên hơn.
7
Huperniketes

Từ blog của bạn, có vẻ như bạn đã quen thuộc với cả lập trình bắt buộc và lập trình chức năng và bạn đã quen thuộc với các khái niệm cơ bản liên quan đến lập trình hướng đối tượng, nhưng bạn chưa bao giờ thực sự "nhấp" vào cái gì làm cho nó hữu ích Tôi sẽ cố gắng giải thích theo kiến ​​thức đó và hy vọng rằng nó hữu ích cho bạn.

Về cốt lõi, OOP là cách sử dụng mô hình mệnh lệnh để quản lý tốt hơn mức độ phức tạp cao bằng cách tạo cấu trúc dữ liệu "thông minh" mô hình hóa miền vấn đề. Trong một (đối tượng thủ tục tiêu chuẩn Chương trình được định hướng), bạn đã có hai điều cơ bản: biến và mã biết phải làm gì với chúng. Mã lấy đầu vào từ người dùng và nhiều nguồn khác, lưu trữ trong biến, vận hành trên đó và tạo dữ liệu đầu ra mà đi đến người dùng hoặc các địa điểm khác.

Lập trình hướng đối tượng là một cách để đơn giản hóa chương trình của bạn bằng cách lấy mẫu cơ bản đó và lặp lại nó ở quy mô nhỏ hơn. Giống như một chương trình là một tập hợp lớn dữ liệu với mã biết phải làm gì với nó, mỗi đối tượng là một phần dữ liệu nhỏ được liên kết với mã để biết phải làm gì với nó.

Bằng cách chia miền vấn đề thành các phần nhỏ hơn và đảm bảo càng nhiều dữ liệu càng tốt được liên kết trực tiếp với mã biết phải làm gì với nó, bạn sẽ dễ dàng suy luận về quy trình nói chung và cả về quy trình phụ. các vấn đề tạo nên quá trình.

Bằng cách nhóm dữ liệu vào các lớp đối tượng, bạn có thể tập trung mã liên quan đến dữ liệu đó, làm cho mã có liên quan dễ dàng hơn để tìm và gỡ lỗi. Và bằng cách đóng gói dữ liệu đằng sau các chỉ định truy cập và chỉ truy cập nó thông qua các phương thức, (hoặc thuộc tính, nếu ngôn ngữ của bạn hỗ trợ chúng), bạn sẽ giảm đáng kể khả năng tham nhũng dữ liệu hoặc vi phạm bất biến.

Và bằng cách sử dụng tính kế thừa và đa hình, bạn có thể sử dụng lại các lớp có sẵn, tùy chỉnh chúng để phù hợp với nhu cầu cụ thể của bạn, mà không phải sửa đổi bản gốc hoặc viết lại mọi thứ từ đầu. (Đó là một điều bạn không bao giờ nên làm , nếu bạn có thể tránh nó.) Hãy cẩn thận bạn hiểu đối tượng cơ sở của mình, hoặc bạn có thể kết thúc với kanguru giết người .

Đối với tôi, đây là những nguyên tắc cơ bản của lập trình hướng đối tượng: quản lý phức tạp, tập trung mã và mô hình hóa miền vấn đề được cải thiện thông qua việc tạo các lớp đối tượng, kế thừa và đa hình, và tăng tính an toàn mà không mất sức mạnh hoặc kiểm soát thông qua việc sử dụng đóng gói và tính chất. Tôi hy vọng điều này giúp bạn hiểu tại sao rất nhiều lập trình viên thấy nó hữu ích.

EDIT: Trả lời câu hỏi của Joel trong các bình luận,

Bạn có thể giải thích "chương trình hướng đối tượng" chứa gì (ngoài những khiếm khuyết ưa thích mà bạn đã vạch ra) về cơ bản khác với một chương trình bắt buộc không? Làm thế nào để bạn "có được quả bóng lăn?"

Một chút từ chối ở đây. Mô hình "chương trình hướng đối tượng" của tôi về cơ bản là mô hình Delphi, rất giống với mô hình C # /. NET do chúng được tạo bởi các thành viên nhóm Delphi trước đây. Những gì tôi đang nói ở đây có thể không áp dụng hoặc không áp dụng nhiều bằng các ngôn ngữ khác OO.

Một chương trình hướng đối tượng là một chương trình trong đó tất cả logic được cấu trúc xung quanh các đối tượng. Tất nhiên điều này phải được bootstrapping ở đâu đó. Chương trình Delphi điển hình của bạn chứa mã khởi tạo tạo một đối tượng singleton có tên Application. Khi bắt đầu chương trình, nó gọi Application.Initialize, sau đó gọi đến Application.CreateForm cho mọi hình thức bạn muốn tải vào bộ nhớ từ đầu, và sau đó Application.Run, sẽ hiển thị biểu mẫu chính trên màn hình và khởi động vòng lặp đầu vào/sự kiện tạo thành cốt lõi của bất kỳ chương trình máy tính tương tác nào.

Ứng dụng và biểu mẫu của bạn thăm dò các sự kiện đến từ HĐH và dịch chúng thành các cuộc gọi phương thức trên đối tượng của bạn. Một điều rất phổ biến là việc sử dụng các trình xử lý sự kiện hoặc "đại biểu" trong .NET-speak. Một đối tượng có một phương thức có nội dung: "thực hiện X và Y, nhưng cũng kiểm tra xem liệu trình xử lý sự kiện cụ thể này có được chỉ định hay không và gọi nó nếu có." Trình xử lý sự kiện là một con trỏ phương thức - một bao đóng rất đơn giản chứa tham chiếu đến phương thức và tham chiếu đến thể hiện đối tượng - được sử dụng để mở rộng hành vi của các đối tượng. Ví dụ: nếu tôi có một đối tượng nút trên biểu mẫu của mình, tôi sẽ tùy chỉnh hành vi của nó bằng cách đính kèm một trình xử lý sự kiện OnClick, điều này khiến một số đối tượng khác thực thi một phương thức khi nhấp vào nút.

Vì vậy, trong một chương trình hướng đối tượng, hầu hết các công việc được thực hiện bằng cách xác định các đối tượng với một số trách nhiệm nhất định và liên kết chúng với nhau, thông qua các con trỏ phương thức hoặc bằng một đối tượng gọi trực tiếp một phương thức được xác định trong giao diện chung của đối tượng khác. (Và bây giờ chúng tôi quay lại đóng gói.) Đây là một ý tưởng mà tôi không có khái niệm về trước khi tôi học OOP các lớp học ở trường đại học.

46
Mason Wheeler

Tôi nghĩ rằng OOP về cơ bản chỉ là một cái tên được đặt cho một cái gì đó mà bạn có thể đã bị cám dỗ để làm trên đường đi, như tôi đã từng.

Quay trở lại khi tôi còn là một lập trình viên nhí, ngay cả ở Fortran, có một thứ giống như một con trỏ đến chương trình con. Nó thực sự hữu ích khi có thể truyền một con trỏ đến chương trình con làm đối số cho chương trình con khác.

Sau đó, điều tiếp theo sẽ thực sự hữu ích là lưu trữ một con trỏ đến chương trình con bên trong bản ghi cấu trúc dữ liệu. Bằng cách đó, bạn có thể nói bản ghi "biết" cách tự thực hiện các thao tác.

Tôi không chắc họ đã từng xây dựng nó thành Fortran chưa, nhưng thật dễ dàng để làm ở C và hậu duệ của nó.

Vì vậy, bên dưới, đó là một ý tưởng đơn giản và hữu ích mà bạn có thể muốn tự làm, và dễ dàng thực hiện bằng các ngôn ngữ gần đây hơn, ngay cả khi một số người biến nó thành một nhóm nhạc khổng lồ đầy những từ thông dụng đáng sợ.

6
Mike Dunlavey

Có nhiều loại OO hệ thống và thật khó để có một định nghĩa mà mọi người sẽ đồng ý. Thay vì cố gắng chỉ ra cách Java OO tương tự như Phổ biến Hệ thống đối tượng LISP, tôi sẽ bắt đầu với một cái gì đó thông thường hơn, từng bước một.

Giả sử bạn có rất nhiều đối tượng tồn tại dưới dạng dữ liệu phân tán. Các điểm, ví dụ, có thể là các phần tử trong một mảng X, Y và Z. Để xem xét một điểm chính nó, sẽ rất hợp lý khi kéo tất cả dữ liệu lại với nhau thành một thứ giống như C struct.

Bây giờ, đối với bất kỳ đối tượng dữ liệu nào, chúng ta đã có dữ liệu cùng nhau. Tuy nhiên, trong một chương trình thủ tục, mã bị phân tán. Giả sử chúng ta đang xử lý các hình dạng hình học. Có một chức năng lớn để vẽ các hình dạng, và nó cần biết về tất cả các hình dạng. Có một chức năng lớn để tìm diện tích và một chức năng khác cho chu vi. Mã cho một vòng tròn được phân tán thông qua nhiều chức năng và để thêm một loại hình dạng khác, chúng ta cần biết những chức năng nào sẽ thay đổi. Trong một hệ thống hướng đối tượng, chúng tôi tập hợp các hàm vào cùng một loại vật (class) làm dữ liệu. Do đó, nếu chúng ta muốn xem tất cả mã vòng tròn, nó sẽ có trong định nghĩa Circle và nếu chúng ta muốn thêm Quartercircle chúng ta chỉ cần viết lớp của nó và chúng ta đã có mã .

Một lợi ích từ việc này là chúng ta có thể duy trì các bất biến của lớp, những điều đúng với từng thành viên của lớp. Bằng cách hạn chế mã bên ngoài lớp không gây rối trực tiếp với các thành viên dữ liệu của lớp, chúng tôi đã có tất cả mã có thể thay đổi dữ liệu của lớp ở một nơi và chúng tôi có thể xác nhận rằng nó không làm bất cứ điều gì khó khăn (như có một hình tam giác với một chân dài hơn hai cái kia cộng lại). Điều này có nghĩa là chúng ta có thể tin tưởng vào một số thuộc tính của mọi thành viên trong lớp và không phải kiểm tra xem liệu một đối tượng có lành mạnh mỗi khi chúng ta sử dụng nó không.

Lợi ích chính đi kèm với sự kế thừa và đa hình. Bằng cách định nghĩa tất cả các hình dạng khác nhau này là các lớp con của một lớp được gọi là Shape, chúng ta có thể điều khiển mã của mình Shapes và đó là công việc của các tiểu dự án hình dạng để làm bất cứ điều gì được gọi bởi các thao tác . Điều này có nghĩa là chúng ta không cần phải chạm vào mã đã kiểm tra cũ khi chúng ta thêm hình dạng mới hoặc tinh chỉnh hành vi của những cái cũ hơn. Chúng tôi tự động có mã cũ có thể trực tiếp tận dụng mã mới. Thay vì làm cho mã điều khiển nhận biết tất cả các hình dạng có thể khác nhau và phải duy trì các chức năng nhận biết tất cả các hình dạng có thể khác nhau, chúng tôi chỉ xử lý các hình dạng và thuộc tính của chúng, trong khi duy trì các lớp con Shape. Điều này đơn giản hóa mã kiểm soát.

Chúng tôi có một số lợi thế ở đây. Vì chúng ta có các bất biến lớp, chúng ta có thể suy luận về các đối tượng dữ liệu lớn hơn giống như cách chúng ta suy luận về các kiểu dựng sẵn, có nghĩa là chúng ta thường có thể chia các khái niệm phức tạp thành các khái niệm đơn giản hơn. Vì mã vòng tròn chủ yếu được chứa trong Circle, nên chúng tôi đã tăng tính cục bộ. Vì không có khái niệm về một vòng tròn nằm rải rác qua một số chức năng khác nhau ở những nơi khác nhau, chúng tôi ít kết nối giữa các thói quen và không phải lo lắng về việc giữ chúng đồng bộ. Vì các lớp, về mặt hiệu quả, các loại, chúng ta có thể tận dụng hệ thống loại hiện có để bắt sử dụng các lớp của chúng ta không tương thích.

5
David Thornley

OO có nhiều định nghĩa khác nhau, vâng. Tôi chắc rằng bạn có thể tự mình tìm thấy rất nhiều thứ này. Cá nhân tôi thích Rees Re: OO như một cách để hiểu ý nghĩa của chúng. Tôi đoán bạn đã đọc nó từ khi bạn trích dẫn Paul Graham. (Tôi giới thiệu nó cho bất kỳ ai quan tâm đến OO.) Tôi sẽ ít nhiều chấp nhận định nghĩa Java ở đây {1,2,3,7,8,9}.

Câu hỏi về tiện ích của OO, đặc biệt là cách tôi tiếp cận nó, xứng đáng có câu trả lời lớn hơn nhiều với vài nghìn dòng mã (một phần để không chỉ là một loạt các xác nhận). Tuy nhiên, đây là một bản tóm tắt của tài liệu giả định đó.

Tôi không nghĩ OO rất hữu ích ở quy mô nhỏ, giả sử, khoảng vài trăm dòng. Đặc biệt, OO ngôn ngữ không có ảnh hưởng chức năng tốt có xu hướng làm cho nó thực sự đau đớn khi thực hiện những điều đơn giản với bất kỳ loại bộ sưu tập hoặc bất cứ thứ gì cần nhiều loại dữ liệu. Đây là lúc hầu hết các mẫu thiết kế xuất hiện; chúng là các thiết bị hỗ trợ cho sức mạnh thấp của nền tảng ngôn ngữ .

Ở khoảng một nghìn dòng, bắt đầu khó theo dõi tất cả các hoạt động và cấu trúc dữ liệu và cách chúng liên quan. Tại thời điểm này, nó giúp có cách tổ chức rõ ràng các cấu trúc và hoạt động dữ liệu, để vẽ ranh giới mô-đun và xác định trách nhiệm và có một cách thuận tiện để hiểu các định nghĩa đó trong khi bạn đang cố gắng lập trình chống lại chúng.

Java-ish OO là một giải pháp nửa chừng cho những vấn đề này đã chiến thắng trong cuộc thi phổ biến. Bởi vì đó là cơ chế tương tự mà Java mọi người áp dụng cho nhỏ Các vấn đề về quy mô được tạo ra bởi một ngôn ngữ không đủ mạnh, nó có xu hướng bắt đầu giống như một giải pháp kỳ diệu cho mọi thứ hơn là một cách để tổ chức. bị mắc kẹt trong C++, hoặc người nào khác (như tôi, làm việc hàng ngày trong C #) sử dụng OO nhưng đừng quá phấn khích về điều đó.

3
Jesse Millikan

OOP = cấu trúc dữ liệu + truyền thông điệp + kế thừa, tất cả đều là các diễn biến logic trong các mô hình lập trình.

OOP có thể được hiểu (bởi các lập trình viên) trong khoảng 90 giây (xem hồ sơ của tôi để biết liên kết). Các khái niệm rất đơn giản.

Làm thế nào để áp dụng nó là một vấn đề khác. Chỉ vì bạn biết cách vung búa không có nghĩa là bạn biết cách thiết kế và xây dựng một ngôi nhà. ;-)

1
Steven A. Lowe

OOP cố gắng mô hình hóa các khái niệm trong thế giới thực về các đối tượng và tương tác giữa chúng. Là con người, chúng ta có xu hướng xử lý thế giới về các đối tượng. Thế giới có đầy đủ các đối tượng có các thuộc tính nhất định và có thể thực hiện các công việc như tương tác với các đối tượng khác. OOP cho phép mô hình hóa thế giới theo các thuật ngữ tương tự. Ví dụ:

  • Người là một đối tượng. Một người có một số tính chất, như tuổi tác và giới tính. Một người có thể làm những việc: ăn, ngủ, lái xe.
  • Xe cũng là một đối tượng (mặc dù thuộc loại khác nhau). Nó cũng có các thuộc tính như make, model và năm. Một chiếc xe có thể làm những việc: di chuyển.

Nhưng một chiếc xe không thể tự di chuyển, nó cần một người lái nó - tương tác giữa các Đối tượng.

1
ysolik

Vì bạn hiểu các cấu trúc, và bạn hiểu các con trỏ hàm và bạn hiểu các cấu trúc với các con trỏ hàm, từ quan điểm của bạn, tôi sẽ định nghĩa lập trình hướng đối tượng là "lập trình, với việc sử dụng nhiều các cấu trúc có con trỏ hàm". Nó vẫn lập trình theo nghĩa truyền thống - đó là tất cả dữ liệu và mã hoạt động theo dữ liệu. Sự khác biệt chỉ đơn giản là cách tất cả các thông tin đó được xác định và cách bạn tiếp cận xác định nó.

Có lẽ một sự đơn giản hóa quá mức là lập trình truyền thống là "mã, với một số cấu trúc dữ liệu" và lập trình hướng đối tượng là "cấu trúc dữ liệu, với một số mã". Cả hai vẫn có cấu trúc dữ liệu và cả hai vẫn có mã. Sau đó, lập trình hướng đối tượng không gì khác hơn là hành động xác định các loại dữ liệu trước và thực thi các hợp đồng về cách chúng giao tiếp thông qua các bộ chức năng.

Như bạn đã quan sát, có một lớp ứng dụng khổng lồ mà đây không phải là cách tuyệt vời để thực hiện giải pháp. Bạn dường như sống trong một thế giới chủ yếu được tạo thành từ các ứng dụng như vậy. Trong bài đăng trên blog của bạn, bạn đề cập đến việc xem xét triển khai vấn đề "99 chai bia" ("chương trình giới thiệu yêu thích" của bạn). 99 chai bia chắc chắn là một phần của thể loại đó. Cố gắng hiểu lập trình hướng đối tượng bằng cách xem xét việc triển khai 99 chai bia giống như cố gắng hiểu kiến ​​trúc nhà cao tầng bằng cách nhìn vào một ngôi nhà trên cây. Ngay cả một ngôi nhà trên cây được xây dựng rất tốt cũng chỉ có thể dạy bạn rất nhiều.

TL; DR: OO giống như lập trình truyền thống, ngoại trừ bạn tập trung nhiều nỗ lực hơn vào việc xác định cấu trúc dữ liệu trước và bạn có các cấu trúc dữ liệu đó giao tiếp với nhau thông qua các con trỏ hàm.

0
Bryan Oakley

Cách đầu tiên tôi hiểu nó là:

Trước khi lập trình hướng đối tượng, bạn đã Lập trình có cấu trúc . Tất cả mọi thứ được tập trung xung quanh quá trình. Câu hỏi đầu tiên bạn phải tự hỏi mình là " Tôi muốn làm gì với thông tin? ".

Với lập trình hướng đối tượng, nó tập trung vào dữ liệu. Câu hỏi đầu tiên bạn phải tự hỏi mình là " Thông tin phù thủy tôi cần phải giải quyết? ". Điều này làm cho sự trừu tượng dễ dàng hơn.

0
DavRob60

Tôi đã viết một bài đăng trên blog cách đây một thời gian mà bạn có thể thấy hữu ích: Thủ tục so với OOP Giải thích .

0
VirtuosiMedia