thảo luận OOP hay FP

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


  • Total voters
    605
Mới đọc được 1 blog khá hay, nói đến việc extend 1 class khác để reuse logic sẽ dẫn đến việc khó design về lâu dài. Trong blog, đề cập đến việc sử dụng template method để thể hiện việc reuse logic, và có thể thay thế bởi loan pattern sử dụng functional programming cụ thể trong blog được implement bằng java 8.
Link: https://dzone.com/articles/functional-programming-patterns-with-java-8
 
Mới đọc được 1 blog khá hay, nói đến việc extend 1 class khác để reuse logic sẽ dẫn đến việc khó design về lâu dài. Trong blog, đề cập đến việc sử dụng template method để thể hiện việc reuse logic, và có thể thay thế bởi loan pattern sử dụng functional programming cụ thể trong blog được implement bằng java 8.
Link: https://dzone.com/articles/functional-programming-patterns-with-java-8
Chuyển ngôn ngữ khác xài luôn cho nhanh chứ Java design của nó với cái mớ class, inherit hầm bà lằng thì ko xài cũng kì :shame:
 
Chuyển ngôn ngữ khác xài luôn cho nhanh chứ Java design của nó với cái mớ class, inherit hầm bà lằng thì ko xài cũng kì :shame:
chuẩn bác, cứ nhắn đến java là mặc định class, OOP. Nhưng FP nó có những ưu điểm riêng nên java mới hỗ trợ, dù biết nó sẽ dẫm chân cái cũ nhưng ngôn ngữ thay đổi để tốt hơn thôi bác. Mặc dù code FP không bằng những ngôn ngữ hỗ trợ FP thuần được :D.
Quote trong blog " we can use the Passing-a-Block(Loan) pattern in Java 8 could be witnessing the funeral of the template method design pattern"
 
giờ học fp thì ứng dụng đc j các thím
và học thì chọn thằng nào haskell hay elm hay elixir, cơ hội việc làm (ngoài nước k tính vn) ntn
so với việc học go lang thì có sáng sủa hơn k
 
Thím @Thích Màu Hường không vào thêm vài change request để anh em xem tiếp nhỉ?
Chính ra thảo luận cứ phải có ví dụ cụ thể như thế mới cho anh em người thường thấy rõ, chứ kiểu chém như các thánh nhân toàn trên mây trên gió anh em chỉ biết ngồi nhìn, chả biết bên nào đúng bên nào sai :D
 
Mới đọc được 1 blog khá hay, nói đến việc extend 1 class khác để reuse logic sẽ dẫn đến việc khó design về lâu dài. Trong blog, đề cập đến việc sử dụng template method để thể hiện việc reuse logic, và có thể thay thế bởi loan pattern sử dụng functional programming cụ thể trong blog được implement bằng java 8.
Link: https://dzone.com/articles/functional-programming-patterns-with-java-8

Desgin càng đơn giản thì càng dễ sửa chửa về lâu dài. Bởi vậy khi nói chuyện design gặp 1 đứa cứ OOP thì cũng khá ức chế, theo mình đây là bệnh chung của nhiều dev do lậm kỹ thuật & thiếu kinh nghiệm thực tế trong thương trường
 
Desgin càng đơn giản thì càng dễ sửa chửa về lâu dài. Bởi vậy khi nói chuyện design gặp 1 đứa cứ OOP thì cũng khá ức chế, theo mình đây là bệnh chung của nhiều dev do lậm kỹ thuật & thiếu kinh nghiệm thực tế trong thương trường
design chuối thì dù là OOP hay OP thì cũng sẽ dẫn tới shit cả thôi. không có cái nào là silver bullet cả.
Như cái bài toán của thím @Thích Màu Hường, mấy bác cuồng FP vẫn chưa có bài giải cho đề số 2.
 
design chuối thì dù là OOP hay OP thì cũng sẽ dẫn tới shit cả thôi. không có cái nào là silver bullet cả.
Như cái bài toán của thím @Thích Màu Hường, mấy bác cuồng FP vẫn chưa có bài giải cho đề số 2.
Có ông còn đặt gạch làm 2 cách mà chưa thấy sủi nhỉ :D

Tôi thử làm 2 đề của thím @Thích Màu Hường theo kiểu FP như này anh em có OK không nha

Code:
# Core - Cannot be modified
def dtable_access_funcs():
    dtable = {}
    def register(cust_type, print_func):
        dtable[cust_type] = print_func
    def get_func(cust_type):
        return dtable[cust_type]
    return register, get_func

register_func, get_func = dtable_access_funcs()

normal_type = "normal"
def print_basic(c):
    print(f"name = {c['name']}")
    print(f"age = {c['age']}")              

def core(c):
    print("#############")

register_func(normal_type, print_basic)    

def print_customer(c, custom_print):
    core(c)
    print_func = get_func(c['type']) if custom_print is None else custom_print
    print_func(c)

Đề 1 là extends thêm 2 loại mới
Code:
# Third party
vip_type = "vip"

def print_vip(c):
    print_basic = get_func(normal_type)
    print_basic(c)
    print(f"vip points = {c['point']}")
    print("@@@@@@@@@@@@@")

register_func(vip_type, print_vip)

# Internal
internal_type = "internal"

def print_internal(c):
    print_basic = get_func(normal_type)
    print_basic(c)
    print(f"department = {c['department']}")
    print("&&&&&&&&&&&&&&")
register_func(internal_type, print_internal)


if __name__ == "__main__":
    customers = [
        { "type": normal_type, "name": "John normal", "age": 40 },
        { "type": vip_type, "name": "Mask VIP", "age": 28, "point": 30 },
        { "type": internal_type, "name": "Internal department", "age": 45, "department": "IT" },
    ]
    for c in customers:
        print_customer(c, None)

Đề 2 là phải mask customer name và print log kiểu AOP cho cái custom print
Code:
masked_type = 'masked'
def mask_customer(customer):
    if customer['age'] < 30:
        masked_customer = dict(customer)
        masked_customer['name'] = '************'
        customer['masked'] = masked_customer
        customer['type'] = masked_type
    return customer

def print_masked_customer(customer):
    if 'masked' in customer:
        masked_customer = customer['masked']
        print_func = get_func(masked_customer['type'])
        print_func(masked_customer)
    else: print_customer(customer)
    
register_func(masked_type, print_masked_customer)

def log_wrap_func(func):
    def log_func(c):
        print(f"[Log] Begin custom print for customer {c['name']}")
        old_print_func = get_func(c['type'])
        old_print_func(c)
        print(f"[Log] End custom print for customer {c['name']}")
    return log_func

if __name__ == "__main__":
    customers = [
        { "type": normal_type, "name": "John normal", "age": 40 },
        { "type": vip_type, "name": "Mask VIP", "age": 28, "point": 30 },
        { "type": internal_type, "name": "Internal department", "age": 45, "department": "IT" },
    ]
    masked_customers = map(mask_customer, customers)
    for c in masked_customers:
        print_customer(c, log_wrap_func(get_func(c['type'])))

Output
Code:
#############
[Log] Begin custom print for customer John normal
name = John normal
age = 40
[Log] End custom print for customer John normal
#############
[Log] Begin custom print for customer Mask VIP
name = ************
age = 28
vip points = 30
@@@@@@@@@@@@@
[Log] End custom print for customer Mask VIP
#############
[Log] Begin custom print for customer Internal department
name = Internal department
age = 45
department = IT
&&&&&&&&&&&&&&
[Log] End custom print for customer Internal department

Phân tích tí:
  • Đề 1 chắc tác giả cho anh em trơn máy nên không có gì khó. Căn bản là thêm behaviour vào 1 hàm có sẵn nên giải pháp đơn giản nhất là gọi cái hàm đó rồi gọi thêm behaviour mới. Yêu cầu lại phụ thuộc vào loại customer nên cho mỗi loại customer 1 cái hàm riêng chắc là ổn.
  • Đề 2 loằng ngoằng hơn chút vì cần thay đổi cho tất cả các loại data, kể cả trường hợp có thêm loại customer mới.
  • Yêu cầu 1 là thay đổi behaviour của 1 hàm (mask cái tên) mà không được sửa code của nó. Behaviour mới này phụ thuộc vào data nên tôi thấy đơn giản hơn cả là xử lý cái data (tên của customer), rồi cái logic mới dựa vào đó để quyết định có che tên hay không.
  • Yêu cầu 2 nghe có vẻ khó hơn là chen ngang 1 bước vào giữa bước gọi core và bước gọi đến phần custom print mà không được động gì đến cái logic này ở module core. OOP xử khá đẹp vì override được phần custom print. Nhưng FP cũng có cách override nếu ông nào code ban đầu design chuẩn để cho phép override cái phần này ở module core. Cụ thể như cách của tôi là cho phép truyền cái hàm custom print khác vào.
Anh em thấy còn sơ hở gì không nhỉ?
 
Có ông còn đặt gạch làm 2 cách mà chưa thấy sủi nhỉ :D

Tôi thử làm 2 đề của thím @Thích Màu Hường theo kiểu FP như này anh em có OK không nha

Code:
# Core - Cannot be modified
def dtable_access_funcs():
    dtable = {}
    def register(cust_type, print_func):
        dtable[cust_type] = print_func
    def get_func(cust_type):
        return dtable[cust_type]
    return register, get_func

register_func, get_func = dtable_access_funcs()

normal_type = "normal"
def print_basic(c):
    print(f"name = {c['name']}")
    print(f"age = {c['age']}")             

def core(c):
    print("#############")

register_func(normal_type, print_basic)   

def print_customer(c, custom_print):
    core(c)
    print_func = get_func(c['type']) if custom_print is None else custom_print
    print_func(c)

Đề 1 là extends thêm 2 loại mới
Code:
# Third party
vip_type = "vip"

def print_vip(c):
    print_basic = get_func(normal_type)
    print_basic(c)
    print(f"vip points = {c['point']}")
    print("@@@@@@@@@@@@@")

register_func(vip_type, print_vip)

# Internal
internal_type = "internal"

def print_internal(c):
    print_basic = get_func(normal_type)
    print_basic(c)
    print(f"department = {c['department']}")
    print("&&&&&&&&&&&&&&")
register_func(internal_type, print_internal)


if __name__ == "__main__":
    customers = [
        { "type": normal_type, "name": "John normal", "age": 40 },
        { "type": vip_type, "name": "Mask VIP", "age": 28, "point": 30 },
        { "type": internal_type, "name": "Internal department", "age": 45, "department": "IT" },
    ]
    for c in customers:
        print_customer(c, None)

Đề 2 là phải mask customer name và print log kiểu AOP cho cái custom print
Code:
masked_type = 'masked'
def mask_customer(customer):
    if customer['age'] < 30:
        masked_customer = dict(customer)
        masked_customer['name'] = '************'
        customer['masked'] = masked_customer
        customer['type'] = masked_type
    return customer

def print_masked_customer(customer):
    if 'masked' in customer:
        masked_customer = customer['masked']
        print_func = get_func(masked_customer['type'])
        print_func(masked_customer)
    else: print_customer(customer)
   
register_func(masked_type, print_masked_customer)

def log_wrap_func(func):
    def log_func(c):
        print(f"[Log] Begin custom print for customer {c['name']}")
        old_print_func = get_func(c['type'])
        old_print_func(c)
        print(f"[Log] End custom print for customer {c['name']}")
    return log_func

if __name__ == "__main__":
    customers = [
        { "type": normal_type, "name": "John normal", "age": 40 },
        { "type": vip_type, "name": "Mask VIP", "age": 28, "point": 30 },
        { "type": internal_type, "name": "Internal department", "age": 45, "department": "IT" },
    ]
    masked_customers = map(mask_customer, customers)
    for c in masked_customers:
        print_customer(c, log_wrap_func(get_func(c['type'])))

Output
Code:
#############
[Log] Begin custom print for customer John normal
name = John normal
age = 40
[Log] End custom print for customer John normal
#############
[Log] Begin custom print for customer Mask VIP
name = ************
age = 28
vip points = 30
@@@@@@@@@@@@@
[Log] End custom print for customer Mask VIP
#############
[Log] Begin custom print for customer Internal department
name = Internal department
age = 45
department = IT
&&&&&&&&&&&&&&
[Log] End custom print for customer Internal department

Phân tích tí:
  • Đề 1 chắc tác giả cho anh em trơn máy nên không có gì khó. Căn bản là thêm behaviour vào 1 hàm có sẵn nên giải pháp đơn giản nhất là gọi cái hàm đó rồi gọi thêm behaviour mới. Yêu cầu lại phụ thuộc vào loại customer nên cho mỗi loại customer 1 cái hàm riêng chắc là ổn.
  • Đề 2 loằng ngoằng hơn chút vì cần thay đổi cho tất cả các loại data, kể cả trường hợp có thêm loại customer mới.
  • Yêu cầu 1 là thay đổi behaviour của 1 hàm (mask cái tên) mà không được sửa code của nó. Behaviour mới này phụ thuộc vào data nên tôi thấy đơn giản hơn cả là xử lý cái data (tên của customer), rồi cái logic mới dựa vào đó để quyết định có che tên hay không.
  • Yêu cầu 2 nghe có vẻ khó hơn là chen ngang 1 bước vào giữa bước gọi core và bước gọi đến phần custom print mà không được động gì đến cái logic này ở module core. OOP xử khá đẹp vì override được phần custom print. Nhưng FP cũng có cách override nếu ông nào code ban đầu design chuẩn để cho phép override cái phần này ở module core. Cụ thể như cách của tôi là cho phép truyền cái hàm custom print khác vào.
Anh em thấy còn sơ hở gì không nhỉ?

Hiện giờ tôi đang làm việc ở cơ quan!
Tối về tôi sẽ phân tích mã nguồn của đồng chí! :)
 
2 đề là gì vậy các thím, lười lội page quá
Rt7E8tX.png
 
@znvdicrd thanks vì đã đào mộ cái topic này lên. Ko liên quan nhưng nhìn cái avatar thì thím cũng chơi Starcraft à. :shame:

Vụ này đã có kết luận như bên dưới, quote lại cho ai chưa đọc:

Thật sự qua 2 bài thì mình thấy khác biệt cơ bản của FP vs OOP là ở những chổ sau:
  1. Pure and immutable: cái này nếu tuân thủ nguyên tắc thì bên FP sẽ phải copy ra object mới mổi khi thay đổi trạng thái (update as copy). Cái này theo mình là trái với vạn vật tự nhiên. Vì như ngoài đời khi người ta nhấn 1 cái nút trên thiết bị nào đó ví dụ như bóng đèn. Cái bóng đèn đó chuyển trạng thái từ tắt sang mở. Hay 1 customer được promote lên VipCustomer thì chỉ thêm VipPoints. Bóng đèn hay customer là cùng 1 object, nó chỉ thay đổi trạng thái theo từng giai đoạn chứ không copy ra 1 instance mới như FP. FP làm vậy chỉ để cache và support multi threads và mình nghĩ nó chỉ có lợi ở trường hợp đó thôi.
  2. Encapsulation: điểm mạnh của OOP là đóng gói data và method nó là obj.method(), còn FP là output = f(input), mặc dù FP tách data và method ra nhưng thực tế vẫn cần sự liên kết giữa data và method. Bởi vậy các ngôn ngữ FP đẽ ra TypeClass, pattern matching để thể hiện kiểu data nào đi với method nào.
  3. Inheritance: Việc không có kế thừa bên FP sẽ bắt buộc các bạn FP tổ chức code theo kiểu composition cả về mặt data và method. Ví dụ về data structure, cái phần chung là BasicInfo phải là 1 struct con của 3 loại customer type. Về kế thừa và override method, các bạn làm FP sẽ phải lồng ghép các function với nhau, ví dụ custom_print_logger => custom_print_masker => custom_print_customer ==(pattern matching)==> real_custom_print_for_specific_customer_type() Cái này một số ngôn ngữ hỗ trợ FP sẽ gọi nó bằng cái tên mỹ miều là Higher Order Function trong khi bên OOP tất cả những hàm print kia đã được tự động gọi do kế thừa và nó được phân biệt bằng type của object. Nếu ai để ý trong bài giải bằng code C# của mình thì mình dùng Decorator pattern chổ masker và logger để vừa dùng composition và vừa dùng inhertiance để override cái hàm CustomPrint như vậy cái chuỗi hàm print đã nói ở trên được tự động gọi trong step 2 của hàm Print khi chạy rồi.
 
@znvdicrd thanks vì đã đào mộ cái topic này lên. Ko liên quan nhưng nhìn cái avatar thì thím cũng chơi Starcraft à. :shame:

Vụ này đã có kết luận như bên dưới, quote lại cho ai chưa đọc:

Thật sự qua 2 bài thì mình thấy khác biệt cơ bản của FP vs OOP là ở những chổ sau:
  1. Pure and immutable: cái này nếu tuân thủ nguyên tắc thì bên FP sẽ phải copy ra object mới mổi khi thay đổi trạng thái (update as copy). Cái này theo mình là trái với vạn vật tự nhiên. Vì như ngoài đời khi người ta nhấn 1 cái nút trên thiết bị nào đó ví dụ như bóng đèn. Cái bóng đèn đó chuyển trạng thái từ tắt sang mở. Hay 1 customer được promote lên VipCustomer thì chỉ thêm VipPoints. Bóng đèn hay customer là cùng 1 object, nó chỉ thay đổi trạng thái theo từng giai đoạn chứ không copy ra 1 instance mới như FP. FP làm vậy chỉ để cache và support multi threads và mình nghĩ nó chỉ có lợi ở trường hợp đó thôi.
  2. Encapsulation: điểm mạnh của OOP là đóng gói data và method nó là obj.method(), còn FP là output = f(input), mặc dù FP tách data và method ra nhưng thực tế vẫn cần sự liên kết giữa data và method. Bởi vậy các ngôn ngữ FP đẽ ra TypeClass, pattern matching để thể hiện kiểu data nào đi với method nào.
  3. Inheritance: Việc không có kế thừa bên FP sẽ bắt buộc các bạn FP tổ chức code theo kiểu composition cả về mặt data và method. Ví dụ về data structure, cái phần chung là BasicInfo phải là 1 struct con của 3 loại customer type. Về kế thừa và override method, các bạn làm FP sẽ phải lồng ghép các function với nhau, ví dụ custom_print_logger => custom_print_masker => custom_print_customer ==(pattern matching)==> real_custom_print_for_specific_customer_type() Cái này một số ngôn ngữ hỗ trợ FP sẽ gọi nó bằng cái tên mỹ miều là Higher Order Function trong khi bên OOP tất cả những hàm print kia đã được tự động gọi do kế thừa và nó được phân biệt bằng type của object. Nếu ai để ý trong bài giải bằng code C# của mình thì mình dùng Decorator pattern chổ masker và logger để vừa dùng composition và vừa dùng inhertiance để override cái hàm CustomPrint như vậy cái chuỗi hàm print đã nói ở trên được tự động gọi trong step 2 của hàm Print khi chạy rồi.

Đa phần các ví dụ và giải thích của bạn ko liên quan ăn nhập gì.

1. Code ko thể đem đối tượng cuộc sống vào so sánh đc, chỉ có các chiến sĩ thấm nhuần OOP mới thích đem mấy cái vd đời thực vào để diễn giải code, để "dễ hiểu". Immutable state là để tránh violate access ví dụ như mấy cái global variable đó.

2. Cái bạn nói ở trên ngta gọi là member method và free function, cách sử dụng và điều kiện sử dụng tuỳ vào bài toán cụ thể. OOP có xu hướng gói tất cả vào member method. Đây chẳng liên quan gì tới Typeclass và Pattern Matching -)).

3. Higher order function cũng chẳng lq gì tới inheritance nốt. Loại bỏ inheritance là xu hướng của các modern lang rồi bạn thích dùng thì cứ dùng, ngta ko cần dùng inheritance vẫn code đc logic đủ đẹp và ngắn gọn. Trait trong Rust cũng cho override đây chả sao.

lol giờ mới nhớ ra là tôi từng hứa giải bài nhưng đã chạy làng. Cũng hơn 3 năm k đụng vào fp.
 
Đa phần các ví dụ và giải thích của bạn ko liên quan ăn nhập gì.

1. Code ko thể đem đối tượng cuộc sống vào so sánh đc, chỉ có các chiến sĩ thấm nhuần OOP mới thích đem mấy cái vd đời thực vào để diễn giải code, để "dễ hiểu". Immutable state là để tránh violate access ví dụ như mấy cái global variable đó.

2. Cái bạn nói ở trên ngta gọi là member method và free function, cách sử dụng và điều kiện sử dụng tuỳ vào bài toán cụ thể. OOP có xu hướng gói tất cả vào member method. Đây chẳng liên quan gì tới Typeclass và Pattern Matching -)).

3. Higher order function cũng chẳng lq gì tới inheritance nốt. Loại bỏ inheritance là xu hướng của các modern lang rồi bạn thích dùng thì cứ dùng, ngta ko cần dùng inheritance vẫn code đc logic đủ đẹp và ngắn gọn. Trait trong Rust cũng cho override đây chả sao.

lol giờ mới nhớ ra là tôi từng hứa giải bài nhưng đã chạy làng. Cũng hơn 3 năm k đụng vào fp.

Ờ, già rồi ko cãi nữa. Bận đi chém gió chuyện xã hội, chính chị chính e rồi. :go:

via theNEXTvoz for iPhone
 
Ờ, già rồi ko cãi nữa. Bận đi chém gió chuyện xã hội, chính chị chính e rồi. :go:

via theNEXTvoz for iPhone

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
 
Js, ts thì người ta thường dùng OOP hay FP hơn vậy mấy fen
Thường dùng FP,
@znvdicrd thanks vì đã đào mộ cái topic này lên. Ko liên quan nhưng nhìn cái avatar thì thím cũng chơi Starcraft à. :shame:

Vụ này đã có kết luận như bên dưới, quote lại cho ai chưa đọc:

Thật sự qua 2 bài thì mình thấy khác biệt cơ bản của FP vs OOP là ở những chổ sau:
  1. Pure and immutable: cái này nếu tuân thủ nguyên tắc thì bên FP sẽ phải copy ra object mới mổi khi thay đổi trạng thái (update as copy). Cái này theo mình là trái với vạn vật tự nhiên. Vì như ngoài đời khi người ta nhấn 1 cái nút trên thiết bị nào đó ví dụ như bóng đèn. Cái bóng đèn đó chuyển trạng thái từ tắt sang mở. Hay 1 customer được promote lên VipCustomer thì chỉ thêm VipPoints. Bóng đèn hay customer là cùng 1 object, nó chỉ thay đổi trạng thái theo từng giai đoạn chứ không copy ra 1 instance mới như FP. FP làm vậy chỉ để cache và support multi threads và mình nghĩ nó chỉ có lợi ở trường hợp đó thôi.
  2. Encapsulation: điểm mạnh của OOP là đóng gói data và method nó là obj.method(), còn FP là output = f(input), mặc dù FP tách data và method ra nhưng thực tế vẫn cần sự liên kết giữa data và method. Bởi vậy các ngôn ngữ FP đẽ ra TypeClass, pattern matching để thể hiện kiểu data nào đi với method nào.
  3. Inheritance: Việc không có kế thừa bên FP sẽ bắt buộc các bạn FP tổ chức code theo kiểu composition cả về mặt data và method. Ví dụ về data structure, cái phần chung là BasicInfo phải là 1 struct con của 3 loại customer type. Về kế thừa và override method, các bạn làm FP sẽ phải lồng ghép các function với nhau, ví dụ custom_print_logger => custom_print_masker => custom_print_customer ==(pattern matching)==> real_custom_print_for_specific_customer_type() Cái này một số ngôn ngữ hỗ trợ FP sẽ gọi nó bằng cái tên mỹ miều là Higher Order Function trong khi bên OOP tất cả những hàm print kia đã được tự động gọi do kế thừa và nó được phân biệt bằng type của object. Nếu ai để ý trong bài giải bằng code C# của mình thì mình dùng Decorator pattern chổ masker và logger để vừa dùng composition và vừa dùng inhertiance để override cái hàm CustomPrint như vậy cái chuỗi hàm print đã nói ở trên được tự động gọi trong step 2 của hàm Print khi chạy rồi.


Theo mình thấy có vài chỗ không hợp lý lắm:
  1. Pure and immutable: Việc này mình thấy hoàn toàn hợp tự nhiên theo nghĩa toán họn. Tức là bạn tắt cái bóng đèn tức là bạn đã làm(cái gì đó) với bóng đèn, SAU KHI bạn làm nó thì cái bóng đèn mới bị tắt. Tức là nếu bạn không chạy Function làm(cái gì đó) thì nó sẽ không bao giờ tắt. Mình thấy bạn hiểu nhầm phần Pure and immutable ở FP, ở đây ko phải là mọi dữ liệu đều bất biến mà mà mọi dữ liệu trong quá trình xử lý thì mới bất biến. Còn thực tế khi làm thì chu trình là Nhận Input => Xử lý => Output => UPDATE DATABASE . Cái bước cuối cùng mới làm thay đổi dữ liệu còn 3 bước đầu dù bạn làm cái gì thì Database sẽ không bao giờ thay đổi. Chỗ này mới là quan trọng, vì ở OOP nếu chẳng may bạn thấy Output ra bị sai thì sẽ tốn nhiều công sức để tìm và xử lý nó. Nhất là vấn đề dây mơ rễ má của Class.

  2. Encapsulation: Việc bạn nói là dĩ nhiên, vì bài toán ở FP đc đưa ra rất rõ rằng là bạn sẽ nhận kiểu dữ liệu nào và giá trị trị ra sao nên công việc của bạn là làm cho OUTPUT matching với từng loại Input. 1 + 1 thì luôn luôn ra 2.
  3. Inheritance: Chưa đào sâu nên không dám phán, nhưng dù sao thì với tôi debug như vầy dễ hơn nhiều chỉ cần chạy test của các function là biết chỗ sửa và không lo sửa chỗ này bị đá chỗ kia như OPP vẫn hay bị.
    C++:
    let func_with_list() =    [5;6;7;8;9;10]
        |> List.filter (fun x -> (x % 2) = 0)
        |> List.map (fun x -> x * 2)
        |> printfn "Even x2 : %A"
        printf ""
 
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
So sánh type lúc runtime thì bình thường, mình cũng gặp nhiều lắm thím, code xấu thôi.

Với trường hợp right click thì liệu có thể làm như thế này không? Trường hợp glow cũng tương tự.

Cách 1: Dùng right click handler làm member của Item, VD một cái là use item, cái kia là remove item. Khi ấy OnRClick của Item sẽ gọi m_RightClickHandler.HandleClick()
Cách 2: Define hàm helper RightClick(IEquipable), để dùng chung code giữa các Equipable với nhau. Tương tự với IConsumable. Cuối cùng Item chỉ cần xử lý OnRightClick một cách generic thôi.

Trường hợp stack thì mình không hiểu yêu cầu của thím nên không dám bàn.

Mình nhìn thì có vẻ thím code C# nên mình chỉ đưa ra các cách làm được bằng C# thôi.
 
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

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
 
So sánh type lúc runtime thì bình thường, mình cũng gặp nhiều lắm thím, code xấu thôi.

Với trường hợp right click thì liệu có thể làm như thế này không? Trường hợp glow cũng tương tự.

Cách 1: Dùng right click handler làm member của Item, VD một cái là use item, cái kia là remove item. Khi ấy OnRClick của Item sẽ gọi m_RightClickHandler.HandleClick()
Cách 2: Define hàm helper RightClick(IEquipable), để dùng chung code giữa các Equipable với nhau. Tương tự với IConsumable. Cuối cùng Item chỉ cần xử lý OnRightClick một cách generic thôi.

Trường hợp stack thì mình không hiểu yêu cầu của thím nên không dám bàn.

Mình nhìn thì có vẻ thím code C# nên mình chỉ đưa ra các cách làm được bằng C# thôi.

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

Chỉ là một vấn đề này trước hay gặp trong code dự án nay nảy ra trong đầu mình thôi :D
Cảm ơn 2 bác nhiều :p
 
Back
Top