thảo luận OOP hay FP

Các Voz Dev thuộc trường phái nào ?


  • Total voters
    596
Js, ts thì người ta thường dùng OOP hay FP hơn vậy mấy fen

Em thấy ở FE thì đi theo structure của fw/lib. Còn BE thì thấy ở project cty vẫn xài class theo kiểu oop, chắc do mấy ông làm C# quen nên dùng class lun

Sent from Vsmart Active 3 using vozFApp
 
Mình thì thấy thuần fp quá cũng hơi khó hiểu/đọc (chắc do trình thấp). OOP hiện đại cũng ít kiểu design kế thừa 3,4 lớp, mà sẽ inplement các behaviour chung qua interface/trait/protocol v.v
 
Để cái poll như bày thì mình nghĩ bạn đang hiểu nhầm về fp. Fp thực ra khuyến khích việc build ra những primitive thật đơn giản (function). Rồi compose linh hoạt những primitive đó để giải quyết vấn đề phức tạp mà mình cần
 
Mình thấy bạn đang tốn năng lượng não để xoắn suýt 1 vấn đề khá vô bổ. Bác tạo ra mấy cái interface cho class item nhìn rất logic theo hướng OOP nhưng thực tế tính thực dụng là số âm.

1. C++ ko hỗ trợ interface, mà các game engine như Unity khi biên dịch sẽ convert sang C++ => chậm hơn 1 chút bởi vì fake bằng proxy.

2. Các game engine hiện tại thường đi theo 2 hướng Component Based Design hoặc Data Oriented Design. Cho nên chẳng ai bắt bẻ dev code ko OOP cả.

=>
  • Nếu là project lớn thì thiết kế hẳn theo hướng Data Oriented, game sẽ chạy nhanh hơn gấp bội.
  • Nếu vội và muốn tiết kiệm thời gian thì cứ tạo ra 1 component item rồi add vào là được

Component kiểu như Unity/Unreal thì vẫn là OOP thôi thím, khi chương trình được tạo nên bởi các object. Component based thì họ đề cao quan hệ kết tập (A has-a B) thay vì kế thừa (B is A) vì nhiều lý do, chủ yếu là để hạn chế kế thừa chồng chéo nhau - thím code C++ rồi chắc biết vấn đề diamond of death khi MoveableObject và DamagableObject cùng kế thừa Object, rồi Character lại kế thừa từ cả MoveableObject và DamagableObject. Componenet based, hay mấy cái tên khác của nó như Entity-Component hoặc GameObject-Component, nó giống như một design pattern ở mức kiến trúc/hệ thống hơn là một paradigm độc lập với OOP.

Còn data oriented design thì chỉ là cách tổ chức dữ liệu, hoàn toàn không liên quan gì tới paradigm OOP hay FP cả - tuy tư tưởng thì có nhiều điểm tương đồng với FP. Ví dụ như việc tập trung vào việc biến đổi dữ liệu của DOD khá giống với công thức output = f(input) của FP. Mấy hệ thống con trong game engine, điển hình như particle system, áp dụng DOD từ lâu rồi. Tương tự với mục trên, DOD hoàn toàn có thể tồn tại song song với OOP, trong cùng một project.
 
Bác @Thích Màu Hường có thể giúp mình 1 cái case này không. Hôm nay mình mới nghĩ đến nó mà thấy hơi bí

Giả sử trong một game chẳng hạn
Item là base

Sword là Item và implement IEquipable
Potion là Item và implement IConsumable

Khi mình duyệt Inventory thì mình đang làm việc với 1 List<Item> . Thời điểm này mình không biết được Item của mình là gì
Giả sử như trong Inventory, IEquipable không thể stack lên nhau, có 1 cái glow màu khác biệt và R-click action đối với IEquipable sẽ khác IConsumable

Trong trường hợp như thế mình thường phải làm
item is IConsumable ?
item is IEquipable ?

Cái này có bình thường khôngtrong OOP, Và nếu nó không bình thường thì làm sao để tránh nó. Vì mình phải tiếp xúc với những code như này khá thường xuyên
nhờ cả bác @tao_la_giang giúp đỡ :D

https://softwareengineering.stackex...ng-the-type-of-a-variable-antithetical-to-oop
Câu hỏi này của qkhanhpro1 cũng là câu hỏi hay thường gặp với OOP mà, không hẳn chỉ gặp với game.
Tuy nhiên, vấn đề mà bạn gặp là của C# chứ không phải là vấn đề với OOP.
Nếu bạn dùng các ngôn ngữ khác có hỗ trợ xử lý dispatching logic (Ruby, Groovy, Smalltalk .. ) thì với trường hợp này, bạn chỉ cần gọi method của IEquiptable (ví dụ: IEquiptable.equip()) lúc duyệt Inventory và thêm logic để xử lý trường hợp Item object không phải là IEquiptable. Hoặc như Objective-C có thể dùng responseToSelector để kiểm tra xem object có triển khai/implement method của IEquiptable
Với cách này, bạn vẫn phải thêm code để xử lý cho từng loại Item, nhưng cách này mới là idiom của OOP chứ không phải cách kiểm tra type (Item is IEquitable? ..)
 
Last edited:
Component kiểu như Unity/Unreal thì vẫn là OOP thôi thím, khi chương trình được tạo nên bởi các object. Component based thì họ đề cao quan hệ kết tập (A has-a B) thay vì kế thừa (B is A) vì nhiều lý do, chủ yếu là để hạn chế kế thừa chồng chéo nhau - thím code C++ rồi chắc biết vấn đề diamond of death khi MoveableObject và DamagableObject cùng kế thừa Object, rồi Character lại kế thừa từ cả MoveableObject và DamagableObject. Componenet based, hay mấy cái tên khác của nó như Entity-Component hoặc GameObject-Component, nó giống như một design pattern ở mức kiến trúc/hệ thống hơn là một paradigm độc lập với OOP.

Còn data oriented design thì chỉ là cách tổ chức dữ liệu, hoàn toàn không liên quan gì tới paradigm OOP hay FP cả - tuy tư tưởng thì có nhiều điểm tương đồng với FP. Ví dụ như việc tập trung vào việc biến đổi dữ liệu của DOD khá giống với công thức output = f(input) của FP. Mấy hệ thống con trong game engine, điển hình như particle system, áp dụng DOD từ lâu rồi. Tương tự với mục trên, DOD hoàn toàn có thể tồn tại song song với OOP, trong cùng một project.

1. Component based thường dùng trong các game engine là vì sao?

  • Lý do ko phải là vì cấu trúc code như kế thừa chồng chéo. Mà do game engine cần quy định sẵn các GameObject để thực hiện rất nhiều low level task ví dụ như redering, culling..

2. Component based độc lập với OOP?
  • Đầu tiên bạn cần phải phân biệt khái niệm OOP với ngôn ngữ lập trình. Theo bạn nói thì bất kỳ class implement System.Object trong C# là object trong OOP, cái này đúng trong hầu hết chương trình vì nó mặc định là như thế.
  • Nhưng trong game engine với Component Based thì nó lại đưa ra khái niệm GameObject mới là Object trong 1 scene. Mà nó lại ko cho kế thừa hay viết lại GameObject mà chỉ có thể thêm bớt Component. Như vậy khi áp dụng khái niệm OOP vào sẽ xung đột. Đương nhiên nếu bạn khăng khăng khẳng định nó là OOP thì cũng đúng ko sai :D

3. OOP cuối cùng cũng là khái niệm, vì nó xoắn suýt tốn time mình thấy rất vô ích nên mới comment có mấy phần phiến diện, chứ bản thân mình vẫn thường dùng OOP nếu thực sự cần.
 
Last edited:
Component kiểu như Unity/Unreal thì vẫn là OOP thôi thím, khi chương trình được tạo nên bởi các object. Component based thì họ đề cao quan hệ kết tập (A has-a B) thay vì kế thừa (B is A) vì nhiều lý do, chủ yếu là để hạn chế kế thừa chồng chéo nhau - thím code C++ rồi chắc biết vấn đề diamond of death khi MoveableObject và DamagableObject cùng kế thừa Object, rồi Character lại kế thừa từ cả MoveableObject và DamagableObject. Componenet based, hay mấy cái tên khác của nó như Entity-Component hoặc GameObject-Component, nó giống như một design pattern ở mức kiến trúc/hệ thống hơn là một paradigm độc lập với OOP.

Còn data oriented design thì chỉ là cách tổ chức dữ liệu, hoàn toàn không liên quan gì tới paradigm OOP hay FP cả - tuy tư tưởng thì có nhiều điểm tương đồng với FP. Ví dụ như việc tập trung vào việc biến đổi dữ liệu của DOD khá giống với công thức output = f(input) của FP. Mấy hệ thống con trong game engine, điển hình như particle system, áp dụng DOD từ lâu rồi. Tương tự với mục trên, DOD hoàn toàn có thể tồn tại song song với OOP, trong cùng một project.

Nói khá hay, +1.

Unreal thì chưa mò, còn Unity thì khá chắc ECS trên đó chưa hoàn thiện.

Tiện thì cho hỏi, ngoài 2 engine kia ra thì b có suggest engine nào có Component based xịn hơn ko?
 
1. Component based thường dùng trong các game engine là vì sao?

  • Lý do ko phải là vì cấu trúc code như kế thừa chồng chéo. Mà do game engine cần quy định sẵn các GameObject để thực hiện rất nhiều low level task ví dụ như redering, culling..

2. Component based độc lập với OOP?
  • Đầu tiên bạn cần phải phân biệt khái niệm OOP với ngôn ngữ lập trình. Theo bạn nói thì bất kỳ class implement System.Object trong C# là object trong OOP, cái này đúng trong hầu hết chương trình vì nó mặc định là như thế.
  • Nhưng trong game engine với Component Based thì nó lại đưa ra khái niệm GameObject mới là Object trong 1 scene. Mà nó lại ko cho kế thừa hay viết lại GameObject mà chỉ có thể thêm bớt Component. Như vậy khi áp dụng khái niệm OOP vào sẽ xung đột. Đương nhiên nếu bạn khăng khăng khẳng định nó là OOP thì cũng đúng ko sai :D

3. OOP cuối cùng cũng là khái niệm, vì nó xoắn suýt tốn time mình thấy rất vô ích nên mới comment có mấy phần phiến diện, chứ bản thân mình vẫn thường dùng OOP nếu thực sự cần.

2. GameObject == Entity à? chỉ là đổi term thôi đúng ko?
 
2. GameObject == Entity à? chỉ là đổi term thôi đúng ko?

GameObject khác Entity. Bạn search google khái niệm Object với Entity xem. Entity mang tính trừu tượng.

Nhìn vào scene hierarchy trong Unity bạn sẽ thấy các đơn vị Object đều là GameObject, mỗi GameObject sẽ chứa các components.
 
GameObject khác Entity. Bạn search google khái niệm Object với Entity xem. Entity mang tính trừu tượng.

Nhìn vào scene hierarchy trong Unity bạn sẽ thấy các đơn vị Object đều là GameObject, mỗi GameObject sẽ chứa các components.

Nói cụ thể trong engine thôi chứ đọc khái niệm + lý thuyết trên mạng thì cả ngàn cái khác nhau rồi ko có cái nào là đúng cả. Theo bạn nói thì GameObject ko cho kế thừa mà chỉ cho add/remove component, thì nó là Entity chứ gì nữa.
Entity có ID, nó là instance chứ trưù tượng chỗ nào?

Tất cả là đang nói đến term thực tế trong game engine nha, chứ đừng dẫn khái niệm.
 
Nói cụ thể trong engine thôi chứ đọc khái niệm + lý thuyết trên mạng thì cả ngàn cái khác nhau rồi ko có cái nào là đúng cả. Theo bạn nói thì GameObject ko cho kế thừa mà chỉ cho add/remove component, thì nó là Entity chứ gì nữa.
Entity có ID, nó là instance chứ trưù tượng chỗ nào?

Tất cả là đang nói đến term thực tế trong game engine nha, chứ đừng dẫn khái niệm.

Thì mình nói thực tế nó là GameObject, ngay cái tên đã thể hiện sẵn rồi. Bạn đang lấy khái niệm chung do 1 nhóm đặt ra để áp vào Unity Engine thì đúng hơn
 
Thì mình nói thực tế nó là GameObject, ngay cái tên đã thể hiện sẵn rồi. Bạn đang lấy khái niệm chung do 1 nhóm đặt ra để áp vào Unity Engine thì đúng hơn

Thì chỉ muốn xem có phải cùng là 1 loại, khác term thôi hay ko thôi. Nếu đúng như mô tả thì GameObject cũng là Entity trong 1 số engine/framework khác đấy.
 
Thì chỉ muốn xem có phải cùng là 1 loại, khác term thôi hay ko thôi. Nếu đúng như mô tả thì GameObject cũng là Entity trong 1 số engine/framework khác đấy.

Game engine mình chỉ xài Unity còn cái khác cũng ko rành lắm.
 
1. Component based thường dùng trong các game engine là vì sao?

  • Lý do ko phải là vì cấu trúc code như kế thừa chồng chéo. Mà do game engine cần quy định sẵn các GameObject để thực hiện rất nhiều low level task ví dụ như redering, culling..

2. Component based độc lập với OOP?
  • Đầu tiên bạn cần phải phân biệt khái niệm OOP với ngôn ngữ lập trình. Theo bạn nói thì bất kỳ class implement System.Object trong C# là object trong OOP, cái này đúng trong hầu hết chương trình vì nó mặc định là như thế.
  • Nhưng trong game engine với Component Based thì nó lại đưa ra khái niệm GameObject mới là Object trong 1 scene. Mà nó lại ko cho kế thừa hay viết lại GameObject mà chỉ có thể thêm bớt Component. Như vậy khi áp dụng khái niệm OOP vào sẽ xung đột. Đương nhiên nếu bạn khăng khăng khẳng định nó là OOP thì cũng đúng ko sai :D

3. OOP cuối cùng cũng là khái niệm, vì nó xoắn suýt tốn time mình thấy rất vô ích nên mới comment có mấy phần phiến diện, chứ bản thân mình vẫn thường dùng OOP nếu thực sự cần.
Giải quyết từng ý nhé
1. Bạn đọc thêm về component design pattern (hay tên trong định nghĩa của GoF là Composite) ở đây. Mục đích chính, ngoài việc hạn chế kế thừa chồng chéo là để cho phép thay đổi hành vi của đối tượng lúc runtime.
https://en.wikipedia.org/wiki/Composite_pattern
https://gameprogrammingpatterns.com/component.html

2. Mình chưa hề nói gì đến ngôn ngữ áp dụng trong bất kỳ comment nào trước đó cả. Nguyên văn ý mình nói là Component, cụ thể là quan hệ GameObject-Component của Unity, chỉ là một design pattern ở mức kiến trúc.

Khi ấy về cơ bản thì mỗi GameObject có một mảng các Component object (quan hệ kết tập của OOP, composition). Các Component class khác nhau như custom MonoBehaviour, MeshRenderer, Rigidbody, BoxCollider, v.v.. đều có parent class, có member variable và có member method.

Định nghĩa của OOP là chương trình được tạo nên bởi các đối tượng, mỗi đối tượng đều đóng gói dữ liệu và hành vi của chúng. Tất cả các đặc tính trên của Unity Scripting API đều thỏa mãn định nghĩa này.

* Ghi chú riêng ý "OOP và ngôn ngữ lập trình" của bạn: "Theo bạn nói thì bất kỳ class implement System.Object trong C# là object trong OOP, cái này đúng trong hầu hết chương trình vì nó mặc định là như thế."
Mình nói "chương trình được tạo nên bởi các object": mình không nhắc tới System.Object của C#, mà mình nói về khái niệm "đối tượng" tổng quát của OOP thôi.

Nói khá hay, +1.

Unreal thì chưa mò, còn Unity thì khá chắc ECS trên đó chưa hoàn thiện.

Tiện thì cho hỏi, ngoài 2 engine kia ra thì b có suggest engine nào có Component based xịn hơn ko?
Component based là một design pattern phổ biến thím à, khá chắc kèo rằng các engine hiện đại đều áp dụng pattern này ở một vài mức độ. Unity thì cấm tiệt kế thừa GameObject như thím @gbvn1 đã nói, nên lập trình viên chỉ kế thừa được một loại Component đặc biệt tên là MonoBehaviour. Unreal thì phức tạp hơn, AActor (tương tự GameObject của Unity) có rất nhiều class con cháu như APawn hay ACharacter, cũng không cấm viết các custom class. Component class cũng có nhiều loại như UActorComponent và USceneComponent.

Còn ECS là một kiến trúc/tư duy khác nữa nhé
- EC (Entity-Component): cái này là tương tự với framework của Unity và Unreal, tuân theo design pattern Composite ở trên, với class Entity bao gồm một collection của các Component instance. Các component có data và behaviour.
VD pseudocode:

C++:
class Entity
{
    Vector3 position; // class Entity hoàn toàn có thể chứa thứ khác ngoài component
    std::vector<Component*> components
};

class Component
{
    Entity* owner;
};

class MoveComponent {

    void Move()
    {
        // move owner using owner->position
    }

};

- ECS (Entity-Component-System): Entity ở đây không phải là một class mà chỉ là một ID, còn Component hoàn toàn chứa data mà thôi. Tưởng tượng nó như một mini relational database ấy, cũng có các phép toán query (entity này có component nào, có bao nhiêu entity có cụm component ABC này,...), add/remove component cho entity. Behaviour giờ đây được đẩy cho các System.
C++:
using EntityId = int32;
using ComponentId = int32;

struct PositionComponent { EntityId owner; Vector3 value; }
struct MoveComponent { EntityId owner; float speed; }
 
^
ECS thì mình biết, và đang làm. Đang muốn tham khảo thêm bọn lib ecs xịn xịn trên các engine thoi. B là code client hay server v?
 
^
ECS thì mình biết, và đang làm. Đang muốn tham khảo thêm bọn lib ecs xịn xịn trên các engine thoi. B là code client hay server v?
À thảo nào thím hỏi engine nào có ECS ngon (mà thím viết nhầm thành component based). Engine thì ít lắm, có mỗi thằng Unity quảng cáo rầm rộ còn mấy thằng phổ biến thì không theo hướng đó.
Còn 3rd party thì nhiều thím à, chủ yếu khác nhau ở mô hình lưu trữ data thôi. C# thì có Entitas, thấy nhiều team cũng giới thiệu cái này. C++ thì có EntityX, flecs và EnTT. Flecs đúng hơn là C chứ ko phải C++.
 
Chẳng là mình mới tập tành làm react Hook rồi có đi tìm hiểu về FP. Rồi thấy có mấy topic bàn luận về vấn đề này trên Medium hay Slack cũng rất thú vị. Nên giờ lên đây lập pool khảo sát các đồng chí VOZ. :big_smile:
Mình thì nghĩ sử dụng linh hoạt giữa OOP VÀ FP. Do có những vần đề thì OOP giải quyết tốt hơn, có những vấn đề thì FP giải quyết tốt hơn.
Ví dụ: Trong project, mình phân chia nhóm chức năng thì tổ chức theo OOP, để tái sử dụng và tùy biến cách tổ chức. Nhưng với 1 chức năng cụ thể thì mình dùng FP.
 
OOP hay FP cũng giống như SQL hay NOSQL thôi :doubt:. Mỗi thằng có đỉểm mạnh điểm yếu tuỳ thuộc vào nhu cầu mà linh động sử dụng chứ sao so sánh thằng nào hơn thằng nào.
 
Back
Top