thảo luận OOP hay FP

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


  • Total voters
    565
Hóng các thánh rồ FP code lại và SO SÁNH CỤ THỂ CHỔ NÀO FP LẠI ƯU VIỆT HƠN OOP cho đề bài trên.
:shame: :shame: :shame:

bài này code chỉ 10 phút nhưng câu này có mà cãi nhau tới năm sau chưa xong :rap:Tư duy của FP nó khác OOP, nên ko thể so đc cái nào ưu việt hơn.

Không liên quan lắm, tôi chưa code FP bao giờ, nhưng ko phủ nhận tư duy immutable rất hay, đang cố gắng áp dụng tư duy đó cho 1 phần nhỏ dự án của tôi. Nhưng cho hỏi anh em FP làm việc với các external resource như socket hay file ổn không nhỉ, performance, async các kiểu ấy?
fp - immutable cơ bản là tốt, nhưng performance sẽ ko đc ngon, phải làm bạn với memoize để tăng performance.
 
bài này code chỉ 10 phút nhưng câu này có mà cãi nhau tới năm sau chưa xong :rap:Tư duy của FP nó khác OOP, nên ko thể so đc cái nào ưu việt hơn.


fp - immutable cơ bản là tốt, nhưng performance sẽ ko đc ngon, phải làm bạn với memoize để tăng performance.

Đó là thím nghĩ như này:

1_AM83LP9sGGjIul3c5hIsWg_OOP.png


Nhưng các anh rồ FP lại nghĩ như này cơ: :beat_brick:

1_AM83LP9sGGjIul3c5hIsWg.png
 
Vừa tìm hiểu hóa ra C# cũng code FP được, đỡ phải làm quen ngôn ngữ mới. Có điều chuyển sang FP đúng tốn bộ nhớ, test thấy sơ sơ Memory x3 so với OOP, 1 phần có thể là do thói quen dùng variable thoải mái cho là chẳng tốn bao nhiêu nhưng giờ mọi thứ đã là immutable. Để lúc nào có thời gian tập code lại mọi thứ = FP xem thế nào, hy vọng cuối tuần. Ước gì mình trẻ lại 10 tuổi khi chưa có gia đình, thèm code quá mà con thì vẫn còn ốm.
 
Vừa tìm hiểu hóa ra C# cũng code FP được, đỡ phải làm quen ngôn ngữ mới. Có điều chuyển sang FP đúng tốn bộ nhớ, test thấy sơ sơ Memory x3 so với OOP, 1 phần có thể là do thói quen dùng variable thoải mái cho là chẳng tốn bao nhiêu nhưng giờ mọi thứ đã là immutable. Để lúc nào có thời gian tập code lại mọi thứ = FP xem thế nào, hy vọng cuối tuần. Ước gì mình trẻ lại 10 tuổi khi chưa có gia đình, thèm code quá mà con thì vẫn còn ốm.

bác có dùng linq ko ? nó là fp đó

.net thì bác thử f# đi, chả cần cài đặt gì đâu. Xem sơ sơ mấy cái tut của mdsn nắm mấy cái cơ bản như bao ngôn ngữ khác
=> qua phần type và pattern matching
=> qua computation expression (tương đương monad) sẽ hiểu cách quản lý side effect

lượt hết bài ở blog này là đủ để thỏa mãn tò mò fp là gì rồi :byebye:
https://fsharpforfunandprofit.com/
 
Mình làm qua 3 công ty lớn. 2 công ty nhỏ. Thì 100% code đều dùng FP. Cá nhân cũng vững FP hơn nên có tí không khách quan. Đánh giá FP code sướng hơn, maintain sau này cũng ok hơn
 
Đề bài:
Tạo hệ thống hiển thị thông tin cho nhiều loại khách hàng. Hiện tại có 2 loại: Customer (Name, Age), VipCustomer(Name, Age, VipPoints) thỏa các yêu cầu sau:
  1. Giả sử phần core hiển thị thông tin Customer là vô cùng phức tạp, ví dụ có tương tác phần cứng, thiết bị đặc biệt. Việc cập nhật, deploy lại sẽ dẫn tới dừng hoạt động kinh doanh. Tóm lại phần này hạn chế, trong đề bài này xem như fixed không được thay đổi. Hoạt động phức tạp đó là in ra các dấu "##############".
  2. Hệ thống này cho phép bên thứ 3 custom cách hiển thị cho từng loại khách hàng thêm vào cái "hoạt động phức tạp" ở mục 1. Cụ thể là sau khi in thông tin sẽ in "$$$$$$$$$$$" đối với loại Customer, in "@@@@@@@" với loại VipCustomer.
  3. Các loại khách hàng có thể phát sinh không giới hạn theo thời gian. Cụ thể sau khi deploy rồi thì có phát sinh InternalCustomer(Name, Age, Deparment) cũng cần được hiển thị thông tin như trong phần core và phần custom là in "&&&&&&&&&&&&"
OOP Implementation
Module 1: CustomerVisualizer.dll: đây là phần core sẽ không được thay đổi, có cho phép bên thứ 3 custom cách hiển thị bằng cách public interface IPrintable với method CustomPrint()
C#:
using System;

namespace CustomerVisualizer
{
    public interface IPrintable
    {
        void CustomPrint();
    }

    public class CustomerVisualizer
    {
        public void Print(IPrintable printable)
        {
            Console.WriteLine("\n#######################################################");
            printable.CustomPrint();    
        }
    }
}

Module 2: CustomerInfoManager.dll: module này chứa thông tin các loại khách hàng và quy định phần hiển thị custom. Module này được cập nhật khi có thêm loại khách hàng mới:
C#:
using CustomerVisualizer;
using System;
using System.Collections.Generic;

namespace CustomerInfoManager
{
    /// <summary>
    /// Base class implement IPrintable để giao tiếp với CustomerVisualizer
    /// </summary>
    public class Customer : IPrintable
    {
        public string Name { get; set; }

        public int Age { get; set; }

        /// <summary>
        /// Custom print được sử dụng trong CustomerVisualizer
        /// </summary>
        public void CustomPrint()
        {
            // Process basic info
            Console.WriteLine($"{nameof(this.Name)} = {this.Name}");

            Console.WriteLine($"{nameof(this.Age)} = {this.Age}");

            // Process other info
            this.PrintOtherInfo();
        }

        protected virtual void PrintOtherInfo()
        {
            Console.WriteLine("$$$$$$$$$$$$$$$$$$$$$$$");
        }
    }

    public class VipCustomer : Customer
    {
        public int VipPoints { get; set; }

        protected override void PrintOtherInfo()
        {
            Console.WriteLine($"{nameof(this.VipPoints)} = {this.VipPoints}");
            Console.WriteLine("@@@@@@@@@@@@@@@@@@@@@@@");
        }
    }

    public class InternalCustomer : Customer
    {
        public string Department { get; set; }

        protected override void PrintOtherInfo()
        {
            Console.WriteLine($"{nameof(this.Department)} = {this.Department}");
            Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&");
        }
    }

    public class CustomerRepo
    {
        /// <summary>
        /// Giả lập lấy data để test
        /// </summary>
        public static IList<Customer> GetCustomers()
        {
            IList<Customer> customers = new List<Customer>
            {
                new Customer { Name = "John Smith", Age = 20 },
                new VipCustomer { Name = "Tim Batte", Age = 30, VipPoints = 100 },
                new InternalCustomer { Name = "Jenny Lars", Age = 40, Department = "IT" }
            };

            return customers;
        }
    }
}

Program: CustomerManagement.exe: cái này là entry point, chứa hàm main để chạy thoy:
C#:
using CustomerInfoManager;
using System.Collections.Generic;

namespace CustomerManagement
{
    class Program
    {
        static void Main(string[] args)
        {
            IList<CustomerInfoManager.Customer> customers = CustomerRepo.GetCustomers();

            CustomerVisualizer.CustomerVisualizer visualizer = new CustomerVisualizer.CustomerVisualizer();
            foreach (CustomerInfoManager.Customer customer in customers)
            {
                visualizer.Print(customer);
            }
        }
    }
}

Tự nhận xét:
Dùng OOP có interface nên có thể public IPrintable.CustomPrint() cho bất kỳ ai muốn custom khi sử dụng CustomerVisualizer.dll. Đây là khả năng mở rộng.

Customer.CustomPrint() in basic info là Name, Age của tất cả các loại khách hàng. Cho dù có 10 loại thì cũng không phải lặp lại 10 lần và đặc biệt là cũng không cần phải gọi lại 10 chổ (do tính kế thừa).

Chương trình có 3 modules, mổi khi thêm loại khách hàng chỉ 1 CustomerInfoManager.dll là phải deploy lại:
CustomerVisualizer.dll - (fixed)
CustomerInfoManager.dll - (updated)
CustomerManagement.exe - (fixed)

Polymorphism hiện rõ cùng là in thông tin nhưng cách custom khác nhau tùy vào loại customer:


Hóng các thánh rồ FP code lại và SO SÁNH CỤ THỂ CHỔ NÀO FP LẠI ƯU VIỆT HƠN OOP cho đề bài trên.
:shame: :shame: :shame:
Ưu việt hơn nó noi hết trong định nghĩa cả 2 rồi , tôi nói rồi code để show cách viết cách reuse thôi , lì vl
Thất bại điển hình nhất là thằng angular , oop tù quá thay đổi éo nổi viết lại cả cái framework từ đầu :]] , angular cũ éo merge lên bản mới dc làm con dân dỗi chuyển hết qua nền tảng khác , kế thừa, đa hình , bao đóng mạnh mẽ là thế đấy,
còn interface là khái niệm chung của lập trình rồi đừng lôi nó như đặc sản của oop vậy , cái cục dll đó nó chỉ xài dc cho project nhà anh thôi, quăng cái dll đó vào project nào chạy cũng dc nhu các package trong fp đi mới có cái mà so sánh ,
 
cái cục dll đó nó chỉ xài dc cho project nhà anh thôi, quăng cái dll đó vào project nào chạy cũng dc nhu các package trong fp đi mới có cái mà so sánh ,

Câu này sai. Nói câu này ra thì tôi biết trình anh cỡ nào rồi.

À trước khi bật lại thì anh tìm hiểu kỹ rồi hay reply nhé.
 
Ưu việt hơn nó noi hết trong định nghĩa cả 2 rồi , tôi nói rồi code để show cách viết cách reuse thôi , lì vl
Thất bại điển hình nhất là thằng angular , oop tù quá thay đổi éo nổi viết lại cả cái framework từ đầu :]] , angular cũ éo merge lên bản mới dc làm con dân dỗi chuyển hết qua nền tảng khác , kế thừa, đa hình , bao đóng mạnh mẽ là thế đấy,
còn interface là khái niệm chung của lập trình rồi đừng lôi nó như đặc sản của oop vậy , cái cục dll đó nó chỉ xài dc cho project nhà anh thôi, quăng cái dll đó vào project nào chạy cũng dc nhu các package trong fp đi mới có cái mà so sánh ,
Tôi thấy ông thiếu kiến thức quá, thiếu kiến thức thì đừng tranh luận. DLL -> dynamic link library ngay cái tên của nó đã nói lên nhiều điều rồi.
 
Câu này sai. Nói câu này ra thì tôi biết trình anh cỡ nào rồi.

À trước khi bật lại thì anh tìm hiểu kỹ rồi hay reply nhé.
Tôi thấy ông thiếu kiến thức quá, thiếu kiến thức thì đừng tranh luận. DLL -> dynamic link library ngay cái tên của nó đã nói lên nhiều điều rồi.
Nhiều khi tranh luận để biết có cái sai thôi , thế anh giải thích xem dll sinh ra để làm gì , để các anh tự hào
 
mới google đây
"
Kỹ thuật đó được gọi là "imports" hoặc "imported functions" từ DLL "library.dll" trong chương trình "example.exe".

Sau đó, các loader code sẽ tìm kiếm "library.dll", và nếu nó tìm thấy"library.dll" thì các tập tin sẽ được tải.

Bên trong tập tin DLL này có chứa một danh sách khác gọi là "export list", danh sách này kết nối các địa chỉ cụ thể cho từng chức năng nằm bên trong tập tin DLL. Kể từ thời điểm này, khi "example.exe" cần gọi một hàm từ "library.dll", "example.exe" chỉ cần sử dụng địa chỉ đó.

"
ủa khác gì cách import package ở ngôn ngữ khác vây , dll là package độc quyền build chạy trên window thôi, tôi hiểu sai dòng nào chỉ hộ để cảm ơn cái
 
Nay rảnh đc xíu code Rust trước cho bà con xem và bình phẩm trước, chỗ tôi hiện chỉ có 3G nên chưa tải GHC về đc, Haskell hẹn một hôm ko xa.

1. visualizer.rs
Code:
pub trait Visualization {
    fn special_print(&self) {
        println!("##############")
    }
    fn customized_print(&self) -> ();

    fn print(&self) -> () {
        self.special_print();
        self.customized_print();
    }
}
}

2. customer.rs
Code:
use crate::visualizer::Visualization;

#[derive(Debug)]
pub struct BasicInfo {
    pub name: String,
    pub age: u8,
}

impl BasicInfo {
    fn print_info(&self) {
        println!("Name = {}", self.name);
        println!("Age = {}", self.age);
    }
}

#[derive(Debug)]
pub struct BasicCustomer {
    pub info: BasicInfo,
}

impl Visualization for BasicCustomer {
    fn customized_print(&self) -> () {
        self.info.print_info();
        println!("$$$$$$$");
    }
}

#[derive(Debug)]
pub struct VipCustomer {
    pub info: BasicInfo,
    pub vip_point: u8,
}

impl Visualization for VipCustomer {
    fn customized_print(&self) -> () {
        self.info.print_info();
        println!("@@@@@@@");
    }
}

#[derive(Debug)]
pub struct InternalCustomer {
    pub info: BasicInfo,
    pub department: String,
}

impl Visualization for InternalCustomer {
    fn customized_print(&self) -> () {
        self.info.print_info();
        println!("&&&&&&&");
    }
}

pub fn get_list_customer() -> Vec<Box<dyn Visualization>> {
    let basic = BasicCustomer {
        info: BasicInfo {
            name: "John Smith".into(),
            age: 20,
        },
    };
    let vip = VipCustomer {
        info: BasicInfo {
            name: "Tim Batte".into(),
            age: 30,
        },
        vip_point: 100,
    };
    let internal = InternalCustomer {
        info: BasicInfo {
            name: "Jenny Lars".into(),
            age: 40,
        },
        department: "IT".into(),
    };

    vec![Box::new(basic), Box::new(vip), Box::new(internal)]
}

3. main.rs
Code:
mod customer;
mod visualizer;

fn main() {
    let list = customer::get_list_customer();
    list.into_iter().for_each(|c| c.print())
}

Nhận xét: Cần dek gì Inheritance bạn toi ơi. Bài này interface + generic là quá đủ.

Đầy đủ output, structure chuẩn như lời hứa, còn lại đẹp xấu hay ngon hơn thì mời bà con bình. Output đây cho ai ko quen đọc code imperative.

Code:
##############
Name = John Smith
Age = 20
$$$$$$$
##############
Name = Tim Batte
Age = 30
@@@@@@@
##############
Name = Jenny Lars
Age = 40
&&&&&&&
 
Last edited:
self.customized_print(); có tính là functional ko bác :v

f# cũng tương tự nè, mình code trong lúc đợi ... build dự án nên code để máy công ty rồi :LOL:
post tạm cái hình. Làm như cách của c# interface kiểu customer.printOtherInfo cũng được nhưng mình ... ko thích làm, làm demo cho mọi người coi thôi. Ai tò mò muốn biết fp thế nào thì cứ lập thread hỏi, mình biết gì thì hỗ trợ náy chứ cũng ko máu hơn thua, vì làm cho tới nơi thì mất công mà mình lại lười.
120850030_779908999521499_8770000497682202139_n.png


chổ cái Test mình viết thêm cho mọi người thấy là ko cần thêm type Test = ...., mấy cái khác là viết cho ai ko quen với du(discriminated unions)

120852393_1327805287566971_8252035008040883703_n.png
 
Last edited:
tôi nhớ là rust có enum type mà nhỉ, rút gọn code được kha khá. tất nhiên làm sao enum cho chuẩn cũng tốn tế bào não phết :/
 
OOP đâu phải là silver bullet, OOP thêm hàm mới khó chết mẹ. Ví dụ chuỗi String, anh thêm hàm mới cho nó thế nào? Ví dụ có chuỗi s = "tủ Lạnh" tôi muốn viết s.myTransform() nó thành "tỦ lẠNH" thì OOP viết thế nào? Kế thừa ra kiểu CustomString rồi thêm hàm myTransform() à? Khá nhảm nhí. Trong khi ko xài OOP thì viết hàm myTransform(s) dễ dàng.

ví dụ khác tôi chỉ có 1 kiểu dữ liệu là mảng pixel, muốn viết thêm 100 filter cho nó trong 10 năm, update 50 lần thì OOP viết thế nào? Kế thừa ra 50 class mới à? Hay phải modify object cũ? Hay viết hàm static rồi gọi đó là OOP? OOP giờ lỗi thời rồi, thời đại của big data thì data-oriented lên ngôi
68747470733a2f2f692e696d6775722e636f6d2f6b493461396c482e6a7067


---

hình này nói rất đúng này
68747470733a2f2f73636f6e74656e742e6673676e322d342e666e612e666263646e2e6e65742f762f74312e31353735322d392f3132303835323339335f313332373830353238373536363937315f383235323033353030383034303838333730335f6e2e706e673f5f6e635f6361743d313031265f6e635f7369643d616539343838265f6e635f6f68633d5046514d305f6f39426251415838312d41745f265f6e635f68743d73636f6e74656e742e6673676e322d342e666e61266f683d6363636332316337356465363861643737613332666430333863383632323965266f653d3546413634323036
 
Nay rảnh đc xíu code Rust trước cho bà con xem và bình phẩm trước, chỗ tôi hiện chỉ có 3G nên chưa tải GHC về đc, Haskell hẹn một hôm ko xa.

1. visualizer.rs
Code:
pub trait Visualization {
    fn special_print(&self) {
        println!("##############")
    }
}

2. customer.rs
Code:
use crate::visualizer::Visualization;
use core::fmt::Debug;

pub trait Printable
where
    Self: Visualization + Debug,
{
    fn customized_print(&self) -> ();

    fn print(&self) -> () {
        self.special_print();
        println!("{:?}", self);
        self.customized_print();
    }
}

#[derive(Debug)]
pub struct BasicInfo {
    pub name: String,
    pub age: u8,
}

#[derive(Debug)]
pub struct BasicCustomer {
    pub info: BasicInfo,
}

impl Visualization for BasicCustomer {}

impl Printable for BasicCustomer {
    fn customized_print(&self) -> () {
        println!("$$$$$$$");
    }
}

#[derive(Debug)]
pub struct VipCustomer {
    pub info: BasicInfo,
    pub vip_point: u8,
}

impl Visualization for VipCustomer {}

impl Printable for VipCustomer {
    fn customized_print(&self) -> () {
        println!("@@@@@@@");
    }
}

#[derive(Debug)]
pub struct InternalCustomer {
    pub info: BasicInfo,
    pub department: String,
}

impl Visualization for InternalCustomer {}

impl Printable for InternalCustomer {
    fn customized_print(&self) -> () {
        println!("&&&&&&&");
    }
}

pub fn get_list_customer() -> Vec<Box<dyn Printable>> {
    let basic = BasicCustomer {
        info: BasicInfo {
            name: "John Smith".into(),
            age: 20,
        },
    };
    let vip = VipCustomer {
        info: BasicInfo {
            name: "Tim Batte".into(),
            age: 30,
        },
        vip_point: 100,
    };
    let internal = InternalCustomer {
        info: BasicInfo {
            name: "Jenny Lars".into(),
            age: 40,
        },
        department: "IT".into(),
    };

    vec![Box::new(basic), Box::new(vip), Box::new(internal)]
}

3. main.rs
Code:
mod customer;
mod visualizer;

fn main() {
    let list = customer::get_list_customer();
    list.into_iter().for_each(|c| c.print())
}

Nhận xét: Cần dek gì Inheritance bạn toi ơi. Bài này interface + generic là quá đủ.

Đầy đủ output, structure chuẩn như lời hứa, còn lại đẹp xấu hay ngon hơn thì mời bà con bình. Output đây cho ai ko quen đọc code imperative.

Code:
##############
BasicCustomer { info: BasicInfo { name: "John Smith", age: 20 } }
$$$$$$$
##############
VipCustomer { info: BasicInfo { name: "Tim Batte", age: 30 }, vip_point: 100 }
@@@@@@@
##############
InternalCustomer { info: BasicInfo { name: "Jenny Lars", age: 40 }, department: "IT" }
&&&&&&&

Thanks thím, chưa xem kỹ vì không biết syntax của Rust phải vừa xem vừa Google nhưng trước khi mọi người so sánh có 2 điểm thím cần update để đúng đề bài:

1. Theo cách tổ chức code như này thì hàm print nằm trong Printable thuộc module customer.rs. Ví dụ có project khác là in thông tin Vozer, in hóa đơn, in employee vốn ko liên quan gì customer nhưng muốn sử dụng hàm print thì phải có dependency là customer.rs. ;) ~~~~~> Cái này thì chỉ cần move Printable vào trong module visualizer.cs là được.

Thấy thím tách ra special với custom print nên nói lại cho rõ: Cái phần custom print là logic con của hàm print dùng để in các fields được thực hiện tuần tự sau phần "đặc biêt" là in "#####". Xem như hàm print là 1 process có 2 steps luôn đi cùng nhau: step 1 in "#####", step 2 in custom. Cái logic 2 steps gói trong hàm print này là cái thuộc phần core và fixed (process flow tuần tự step 1 -> step 2 không được thay đổi) và nó phải được reuse ở các project khác trong đó step 2 có thể cho người ta custom.

2. Cái phần thông tin chi tiết (Name, Age, VipPoints, Department) phải in ra từng field ra output đúng format như bên C# (tôi đã update cái ouput của code C# trong post đề bài). Hiện tài thím dùng println!("{:?}", self); là ăn gian. Cái phần này phải move nó vào trong custom print để đảm bảo không break process flow 2 steps của hàm print như đã nói ở trên với lại có thể format lúc in cho từng loại customer dc như bên code C#. ~~~~~~> làm vậy thì không có kế thừa thím phải gọi hàm nhiều chổ lúc in BasicInfo ;)

P/s: sau cái này có thể tôi sẽ ví dụ thêm nhiều cái change requests dể giả lập implement mấy cái AOP như validation, authorized, logging túm lại thêm nhiều layer xung quanh business domain xem lúc project scale tới tầm enterprise các FP expert sửa code bao nhiêu chổ hay đập đi xây lai. ;)
 
Back
Top