HIL
Senior Member
Just another implementation in F#Đề Bài (phần 1):
thảo luận - OOP hay FP (https://voz.vn/t/oop-hay-fp.137397/page-10#post-4525643)
Đề Bài (phần 2):
Giả sử phần custom print (business logic để custom cái step 2 cho hàm Print) đã được làm và chạy ổn định, việc sửa code trong module này cực kỳ nguy hiểm, đòi hỏi nhiều chi phí để test lại. Trong đề bài phần này xem như không được thay đổi. Như vậy hiện tại có 2 modules fixed là Visualizer (phần core) và CustomerInfoManager (chứa loại khách hàng và custom print).
Với điều kiện trên Implement thêm 2 yêu cầu sau:
4. Đối với khách hàng có Age <= 30, lúc in Name không được in tên thật mà phải mask thành các dấu "******".
5. Cần ghi log trước và sau khi gọi custom print cho từng khách hàng. Format như sau, xem phần output cho rõ:
[Log]: Begin CustomPrint for customer name = <Name>
// In phần custom như lúc đợt 1
[Log]: End CustomPrint for customer name = <Name>
Mã:
module Visualizer =
type CustomPrint = unit -> unit
let print (customPrint: CustomPrint) =
"\n#######################################################"
|> System.Console.WriteLine
customPrint ()
module CustomerInfoManager =
open Visualizer
type StandardCustomer = { Name: string; Age: int }
type VipCustomer = { Name: string; Age: int; VipPoints: int }
type InternalCustomer = { Name: string; Age: int; Department: string }
type Customer =
| Standard of StandardCustomer
| VIP of VipCustomer
| Internal of InternalCustomer
module Customer =
let name c =
match c with
| Standard s -> s.Name
| VIP v -> v.Name
| Internal i -> i.Name
let age c =
match c with
| Standard s -> s.Age
| VIP v -> v.Age
| Internal i -> i.Age
let printOtherInfo c =
match c with
| Standard _ -> "$$$$$$$$$$$$$$$$$$$$$$$"
| VIP v -> sprintf "VipPoints = %i\n@@@@@@@@@@@@@@@@@@@@@@@" v.VipPoints
| Internal i -> sprintf "Department = %s\n&&&&&&&&&&&&&&&&&&&&&&&" i.Department
let customPrint c : CustomPrint =
fun () ->
sprintf "Name = %s\nAge = %i\n%s" (c |> name) (c |> age) (c |> printOtherInfo)
|> System.Console.WriteLine
module CustomerRepo =
let getCustomers () =
[ Standard { Name = "John Smith"; Age = 20 }
VIP { Name = "Tim Batte"; Age = 30; VipPoints = 100 }
Internal { Name = "Jenny Lars"; Age = 40; Department = "IT" } ]
module CrossCuttingConcerns =
open CustomerInfoManager
open Visualizer
let maskNameIfSmallAge customPrint c : CustomPrint =
let mask text c =
match c with
| Standard s -> { s with Name = text } |> Standard
| VIP v -> { v with Name = text } |> VIP
| Internal i -> { i with Name = text } |> Internal
if c |> Customer.age <= 30 then c |> mask "******" else c
|> customPrint
let log customPrint c : CustomPrint =
fun () ->
sprintf "[Log]: Begin CustomPrint for customer name = %s" (c |> Customer.name)
|> System.Console.WriteLine
customPrint c ()
sprintf "[Log]: End CustomPrint for customer name = %s" (c |> Customer.name)
|> System.Console.WriteLine
module CustomerManagement =
open CrossCuttingConcerns
open CustomerInfoManager
let main () =
CustomerRepo.getCustomers ()
|> List.map (Customer.customPrint |> maskNameIfSmallAge |> log)
|> List.map Visualizer.print
|> ignore
CustomerManagement.main ()
Mã:
module Visualizer =
type CustomPrint = unit -> unit
let print (customPrint: CustomPrint) =
"\n#######################################################"
|> System.Console.WriteLine
customPrint ()
module CustomerInfoManager =
open Visualizer
type Customer(name, age) =
member val Name = name with get, set
member val Age = age with get, set
abstract PrintOtherInfo: string
default this.PrintOtherInfo = "$$$$$$$$$$$$$$$$$$$$$$$"
type VipCustomer(name, age, vipPoints: int) =
inherit Customer(name, age)
member val VipPoints = vipPoints
override this.PrintOtherInfo = sprintf "VipPoints = %i\n@@@@@@@@@@@@@@@@@@@@@@@" this.VipPoints
type InternalCustomer(name, age, department) =
inherit Customer(name, age)
member val Department = department
override this.PrintOtherInfo = sprintf "Department = %s\n&&&&&&&&&&&&&&&&&&&&&&&" this.Department
module Customer =
let customPrint (c: Customer) : CustomPrint =
fun () ->
sprintf "Name = %s\nAge = %i\n%s" c.Name c.Age c.PrintOtherInfo
|> System.Console.WriteLine
module CustomerRepo =
let getCustomers () : Customer list =
[ Customer("John Smith", 20)
VipCustomer("Tim Batte", 30, 100)
InternalCustomer("Jenny Lars", 40, "IT") ]
module CrossCuttingConcerns =
open CustomerInfoManager
open Visualizer
let maskNameIfSmallAge customPrint (c: Customer) : CustomPrint =
fun () ->
let name = c.Name
if c.Age <= 30 then c.Name <- "******" else ()
customPrint c ()
c.Name <- name
let log customPrint (c: Customer) : CustomPrint =
fun () ->
sprintf "[Log]: Begin CustomPrint for customer name = %s" c.Name
|> System.Console.WriteLine
customPrint c ()
sprintf "[Log]: End CustomPrint for customer name = %s" c.Name
|> System.Console.WriteLine
module CustomerManagement =
open CrossCuttingConcerns
open CustomerInfoManager
let main () =
CustomerRepo.getCustomers ()
|> List.map (Customer.customPrint |> maskNameIfSmallAge |> log)
|> List.map Visualizer.print
|> ignore
CustomerManagement.main ()
Kiểm tra tính đúng đắn của nghiệp vụ: phệt code vào đây
Try F#
To be continued: Tại sao lại có hybrid approach?
Sửa lần cuối:

