thảo luận OOP hay FP

  • Người tạo chủ đề Người tạo chủ đề Quynh 123
  • Ngày bắt đầu Ngày bắt đầu

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


  • Tổng số người bình chọn
    684
Mình đang tự học FP, dự định convert project từ OOP sang FP mà đang bế tắc với caching. Ở OOP thì bình thường sẽ viết 1 cái interface thế này


Vậy với FP phải làm như thế nào nhỉ?
không biết hỏi thật hay hỏi móc.
thắc mắc implementation thì còn hiểu được, đây lại thắc mắc api.
FP cũng viết y vậy thôi.

Mã:
module LRUCache =

    let get<'a> (key: string) (acquire: () -> 'a) =
         // implement

một số điểm lưu ý

  • không khai báo interface, implement luôn.
  • FP không có default parameter, nên anh phải tạo 1 hàm khác kiểu như là getWithCacheTime
  • cái dictionary lưu cache anh có thể tạo trong module rồi get, set... truy cập vào nó (y như OOP); hoặc anh có thể truyền vào làm tham số của get, set... rồi anh tạo những hàm get, set khác "bake-in" cái dictionary này.
  • cái module này là singleton, nơi khác anh import rồi dùng, không DI gì cả.
 
không biết hỏi thật hay hỏi móc.
thắc mắc implementation thì còn hiểu được, đây lại thắc mắc api.
FP cũng viết y vậy thôi.

Mã:
module LRUCache =

    let get<'a> (key: string) (acquire: () -> 'a) =
         // implement

một số điểm lưu ý

  • không khai báo interface, implement luôn.
  • FP không có default parameter, nên anh phải tạo 1 hàm khác kiểu như là getWithCacheTime
  • cái dictionary lưu cache anh có thể tạo trong module rồi get, set... truy cập vào nó (y như OOP); hoặc anh có thể truyền vào làm tham số của get, set... rồi anh tạo những hàm get, set khác "bake-in" cái dictionary này.
  • cái module này là singleton, nơi khác anh import rồi dùng, không DI gì cả.
Sorry bạn vì câu hỏi không rõ ràng. Mình viết tường minh lại như sau.

Bây giờ mình cần thêm caching vào hệ thống có sẵn chẳng hạn, thì với OOP cách nghĩ nó là như sau:

1) Viết interface
2) Viết implementation cụ thể với mỗi kiểu cache khác nhau. Ví dụ có thể tự viết 1 cái simple caching dùng dictionary như bạn nói (hay chính xác là 1 thread safe dictionary, trong C# nó gọi là ConcurrentDictionary), hoặc sử dụng của bên thứ 3 ví dụ như Redis chẳng hạn, cái gì cần thì tống vào constructor.
3) Dùng 1 cái IoC nào đấy, autofac chẳng hạn, để quản lý việc dùng implementation cụ thể là cái nào.

Giờ chuyển sang FP mình thậm chí còn chưa biết sẽ phải làm những cái gì, bắt đầu từ đâu, như thế nào. Câu hỏi của mình ko liên quan đến việc so sánh OOP vs FP mà thuần túy là vấn đề cá nhân, học sinh lớp 1 tập đánh vần.
 
Sorry bạn vì câu hỏi không rõ ràng. Mình viết tường minh lại như sau.

Bây giờ mình cần thêm caching vào hệ thống có sẵn chẳng hạn, thì với OOP cách nghĩ nó là như sau:

1) Viết interface
2) Viết implementation cụ thể với mỗi kiểu cache khác nhau. Ví dụ có thể tự viết 1 cái simple caching dùng dictionary như bạn nói (hay chính xác là 1 thread safe dictionary, trong C# nó gọi là ConcurrentDictionary), hoặc sử dụng của bên thứ 3 ví dụ như Redis chẳng hạn, cái gì cần thì tống vào constructor.
3) Dùng 1 cái IoC nào đấy, autofac chẳng hạn, để quản lý việc dùng implementation cụ thể là cái nào.

Giờ chuyển sang FP mình thậm chí còn chưa biết sẽ phải làm những cái gì, bắt đầu từ đâu, như thế nào. Câu hỏi của mình ko liên quan đến việc so sánh OOP vs FP mà thuần túy là vấn đề cá nhân, học sinh lớp 1 tập đánh vần.
xin trả lời bạn là.

với FP bạn không viết interface mà bạn viết implementation (trong module) và bạn mang module đấy import tới những nơi cần sử dụng.

việc implementation giống hệt với bên OOP thôi. thread safe hay không là do implementation của bạn.

nói ngắn gọn là cái module cache đó là singleton, bạn import trực tiếp tới nơi cần sử dụng, không DI, IOC container gì cả.
 
Sorry bạn vì câu hỏi không rõ ràng. Mình viết tường minh lại như sau.

Bây giờ mình cần thêm caching vào hệ thống có sẵn chẳng hạn, thì với OOP cách nghĩ nó là như sau:

1) Viết interface
2) Viết implementation cụ thể với mỗi kiểu cache khác nhau. Ví dụ có thể tự viết 1 cái simple caching dùng dictionary như bạn nói (hay chính xác là 1 thread safe dictionary, trong C# nó gọi là ConcurrentDictionary), hoặc sử dụng của bên thứ 3 ví dụ như Redis chẳng hạn, cái gì cần thì tống vào constructor.
3) Dùng 1 cái IoC nào đấy, autofac chẳng hạn, để quản lý việc dùng implementation cụ thể là cái nào.

Giờ chuyển sang FP mình thậm chí còn chưa biết sẽ phải làm những cái gì, bắt đầu từ đâu, như thế nào. Câu hỏi của mình ko liên quan đến việc so sánh OOP vs FP mà thuần túy là vấn đề cá nhân, học sinh lớp 1 tập đánh vần.
Bài của bạn khá là hay. Thực ra nếu bạn code bằng OOP thì bạn vẫn sử dụng các tính năng như OOP như thường. Ở đây nếu dùng FP sẽ hơn được một việc là bạn biết chỗ nào là thread safe và đoạn nào là non-thread safe và cách để xử lý đoạn đó.

Đối với polymorphism, ở đây polymorphism của FP không khác gì OOP vì bài này quá đơn giản nếu làm chi tiết sẽ overkill.

Còn về IoC, mình chưa dùng autofac bao giờ nhưng theo mình hiểu là bạn muốn có một thư viện quản lý DI tự động. Đối với mình thì IoC/DI vẫn làm bằng tay như thường, bạn tạo interface và tự inject class mà bạn muốn vào trong constructor. Còn theo quan điểm riêng của mình mình sẽ không sử dụng thư viện ở đây vì sử dụng thư viện sẽ có cái bất cập của sử dụng thư viện. Runtime của bạn sẽ bị phụ thuộc vào thư viện và sẽ khó customize hơn khi cần thiết, ví dụ trong Typescript mình có thể hoàn toàn có thể DI bằng tay thông qua interface mà thư viện của mình có thể chạy trên cả hai runtime khác nhau là Node và Deno.

Ở trong các ngôn ngữ OOP thường mình sẽ sử dụng thư viện DI vì hai vấn đề sau, một là không thể truyền quá nhiều tham số vào constructor thay vì gọi một hàm chung để lấy ra các dependency như thư viện thì sẽ đơn giản hơn rất nhiều. Hai là sử dụng thư viện sẽ dễ dàng quản lý các component theo mô hình singleton, đảm bảo các service được khởi tạo duy nhất đúng một lần.

P/s: Tóm lại là bài này cách implement của bạn sẽ không khác gì OOP cả! =))
 
Có một điểm mình cũng khuyên các bạn theo FP nên tránh là convert toàn bộ Object trong OOP thành nhiều function hoặc sử dụng static function thay cho method vì như vậy là đi ngược với mô hình phát triển của lập trình và việc convert toàn bộ cũng là phi lý. Mình cũng mong nhiều bạn đừng vì nhìn vào chữ Functional mà vội đưa ra đánh giá hay kết luận sai lầm. Tuy cách giải quyết vấn đề của FP và OOP có thể hơi khác nhau nhưng cả hai paradigm đều có mục tiêu chung và không hoàn toàn overlap hay loại trừ lẫn nhau.

Bạn nào tìm hiểu FP nên hiểu về pure function và side effect trước khi học các vấn đề khác. Sau đó về type system thì các bạn nên tìm hiểu Haskell, bạn sẽ biết làm thế nào để tạo ra abstraction đủ cao để khó có thể gây lỗi và dễ dàng hơn cho compiler để optimize hiệu năng, bạn cũng sẽ biết cách để module hóa logic như thế nào là hợp lý nhất có thể.

Sau đó bạn mới có thể hiểu tại sao ở trong FP người ta lại không dùng for (let i = 0; i < 10; i++) hay for (const item of collection) gì đó mà lại dùng hàm forEach map và filter, làm cách nào để các compiler của các ngôn ngữ FP optimize mà không bị dính side-effect và cách cải tiến hiệu năng cho các hàm trên sử dụng kĩ thuật lazy evaluation.

Edit: Đúng là hiện giờ các ngôn ngữ OOP vẫn là phổ biến nhất và các ngôn ngữ FP mới dần phát triển theo sau nhưng cũng không có nghĩa là sử dụng FP không có ý nghĩa. Hiện giờ các ngôn ngữ OOP cũng cập nhật khá nhiều các tính năng của FP. Kể cả bình thường khi mình code OOP mình vẫn áp dụng rất nhiều các kĩ thuật code của FP mà có thể nhiều bạn bạn code OOP không biết đến. Đương nhiên việc dùng FP trên ngôn ngữ FP vẫn lợi hơn rất nhiều tuy nhiên đấy là vấn đề của sau này, chưa phải vấn đề các bạn sẽ nghĩ tới mai hoặc ngay bây giờ.
 
Sửa lần cuối:
Có một điểm mình cũng khuyên các bạn theo FP nên tránh là convert toàn bộ Object trong OOP thành nhiều function hoặc sử dụng static function thay cho method vì như vậy là đi ngược với mô hình phát triển của lập trình và việc convert toàn bộ cũng là phi lý. Tuy cách giải quyết vấn đề của FP và OOP có thể hơi khác nhau nhưng cả hai paradigm đều có mục tiêu chung và không hoàn toàn overlap hay loại trừ lẫn nhau.

Bạn nào tìm hiểu FP nên hiểu về pure function và side effect trước khi học các vấn đề khác. Sau đó về type system thì các bạn nên tìm hiểu Haskell, bạn sẽ biết làm thế nào để tạo ra abstraction đủ cao để khó có thể gây lỗi và dễ dàng hơn cho compiler để optimize hiệu năng, bạn cũng sẽ biết cách để module hóa logic như thế nào là hợp lý nhất có thể.

Sau đó bạn mới có thể hiểu tại sao ở trong FP người ta lại không dùng for (let i = 0; i < 10; i++) hay for (const item of collection) gì đó mà lại dùng hàm forEach map và filter, làm cách nào để các compiler của các ngôn ngữ FP optimize mà không bị dính side-effect và cách cải tiến hiệu năng cho các hàm trên sử dụng kĩ thuật lazy evaluation.
Anh nói từ đầu như này là ưng cái bụng liền nè.
 
Có một vấn đề nữa khá hay mình cũng muốn đem ra thảo luận trao đổi chút cho vui. Trước đây khi mình học về DI thì Google có recommend là không nên để tất cả logic vào trong constructor của class vì một lý do, nếu constructor gặp exception thì class đó sẽ bị rơi vào trạng thái không xác định và rất khó để debug (callstack thường không hiển thị đúng vị trí gây ra lỗi và lý do gây lỗi). Do đó constructor của class thường thin nhất có thể và thường chỉ sử dụng để khởi tạo giá trị, không sử dụng để thực hiện logic khởi tạo phức tạp. Vậy FP xử lý bài toán này như thế nào? Trong OOP có mô hình nào tương tự xử lý bài toán này hay không? Các bạn cho ý kiến =))
 
Có một vấn đề nữa khá hay mình cũng muốn đem ra thảo luận trao đổi chút cho vui. Trước đây khi mình học về DI thì Google có recommend là không nên để tất cả logic vào trong constructor của class vì một lý do, nếu constructor gặp exception thì class đó sẽ bị rơi vào trạng thái không xác định và rất khó để debug (callstack thường không hiển thị đúng vị trí gây ra lỗi và lý do gây lỗi). Do đó constructor của class thường thin nhất có thể và thường chỉ sử dụng để khởi tạo giá trị, không sử dụng để thực hiện logic khởi tạo phức tạp. Vậy FP xử lý bài toán này như thế nào? Trong OOP có mô hình nào tương tự xử lý bài toán này hay không? Các bạn cho ý kiến =))
Thường thì trong constructor không có logic gì cả. Nếu muốn tạo object với logic phức tạp thì dùng builder pattern hoặc factory pattern
 
Thường thì trong constructor không có logic gì cả. Nếu muốn tạo object với logic phức tạp thì dùng builder pattern hoặc factory pattern
Thực ra dùng Builder Pattern với Factory hơi overkill quá, nói thế mọi người lại convert hết toàn bộ code thành Builder với Factory hết thì khổ. Builder thường chỉ dùng khi constructor có quá nhiều tham số. Còn Factory thì dùng khi kết quả trả về có nhiều trường hợp hay nhiều kiểu khác nhau.

Bài này thường sẽ có hai cách giải quyết, một là bạn tách logic khởi tạo ra thành một static method riêng, object sẽ chỉ được trả về chỉ khi khởi tạo thành công. Ngược lại mình sẽ trả về null hoặc fallback object. Cách thứ hai là bạn tách logic khởi tạo thành một method riêng và gọi sau khi construct object.
 
Còn về IoC, mình chưa dùng autofac bao giờ nhưng theo mình hiểu là bạn muốn có một thư viện quản lý DI tự động. Đối với mình thì IoC/DI vẫn làm bằng tay như thường, bạn tạo interface và tự inject class mà bạn muốn vào trong constructor. Còn theo quan điểm riêng của mình mình sẽ không sử dụng thư viện ở đây vì sử dụng thư viện sẽ có cái bất cập của sử dụng thư viện. Runtime của bạn sẽ bị phụ thuộc vào thư viện và sẽ khó customize hơn khi cần thiết, ví dụ trong Typescript mình có thể hoàn toàn có thể DI bằng tay thông qua interface mà thư viện của mình có thể chạy trên cả hai runtime khác nhau là Node và Deno.
IoC Container công dụng chính của nó là để resolve dependency graph.
Ví dụ có các component A->B, A->C, A->D, C->D, C->E
Thì dùng IoC Container nó sẽ biết tạo E,D,C,B,A theo thứ tự.
Cái này hoàn toàn có thể code bằng tay nhưng khi số lượng component phình ra, dependency graph càng ngày càng phức tạp thì làm manually là tốn thời gian và khó refactor (như cái spring số lượng bean chắc tới cả trăm, dev chỉ cần inject vô là có sẵn mà dùng).

Ví dụ 1 bài toán sau:
Mình cần tạo 1 class JdbcTemplate để truy xuất database, rõ ràng muốn khởi tạo class này thì cần thêm các thông số db url... password connection...)
Ok, chuyện này dễ mình có thử tự bỏ đống dependency đó thủ công.
Nhưng giả xử lại có 1 thằng lib thứ ba khác cũng muốn dùng lại thằng JdbcTemplate này (ví dụ scheduler...), lúc này chẳng lẽ mình phải tự inject cái thằng JdBcTemplate vào thằng scheduler.
Rồi giả sử không phải 2 thằng mà là 10 thằng nó depends chồng chéo lên nhau thì sau
yBBewst.png
.

Tóm lại IoC Container đã giải quyết được bài toán này gần như tuyệt vời, anh cần cái nào thì anh cứ khai báo cái đó, còn việc khởi tạo cái đó ra sao thì tôi sẽ làm giúp anh
FfsqRRV.png
.

Còn tuyệt vời hơn nữa là Spring Boot, tôi muốn đổi Cache JPA, Servlet Implementation... tôi chỉ cần đổi class path (ví dụ thêm thư viện redis vào), không cần sửa 1 dòng code, không cần phải chơi mấy trò plugin, middleware
zFNuZTA.png


1 bài toán khá hay( mặc dù là ít áp dụng) là plugin system. Ví dụ tôi muốn thêm intercepter vào app của tôi. Trong Java dùng IoC Container tui chỉ cần compile cái intercepter của tôi và thêm vào classpath chạy lại app, không cần phải sửa code gì hết
zFNuZTA.png


Không biết bên Functional giải quyết như thế nào
BdgiW7R.png
 
Sửa lần cuối:
IoC Container công dụng chính của nó là để resolve dependency graph.
Ví dụ có các component A->B, A->C, A->D, C->D, C->E
Thì dùng IoC Container nó sẽ biết tạo E,D,C,B,A theo thứ tự.
Cái này hoàn toàn có thể code bằng tay nhưng khi số lượng component phình ra, dependency graph càng ngày càng phức tạp thì làm manually là tốn thời gian và khó refactor (như cái spring số lượng bean chắc tới cả trăm, dev chỉ cần inject vô là có sẵn mà dùng).

Ví dụ 1 bài toán sau:
Mình cần tạo 1 class JdbcTemplate để truy xuất database, rõ ràng muốn khởi tạo class này thì cần thêm các thông số db url... password connection...)
Ok, chuyện này dễ mình có thử tự bỏ đống dependency đó thủ công.
Nhưng giả xử lại có 1 thằng lib thứ ba khác cũng muốn dùng lại thằng JdbcTemplate này (ví dụ scheduler...), lúc này chẳng lẽ mình phải tự inject cái thằng JdBcTemplate vào thằng scheduler.
Rồi giả sử không phải 2 thằng mà là 10 thằng nó depends chồng chéo lên nhau thì sau
yBBewst.png
.

Tóm lại IoC Container đã giải quyết được bài toán này gần như tuyệt vời, anh cần cái nào thì anh cứ khai báo cái đó, còn việc khởi tạo cái đó ra sao thì tôi sẽ làm giúp anh
FfsqRRV.png
.

Còn tuyệt vời hơn nữa là Spring Boot, tôi muốn đổi Cache JPA, Servlet Implementation... tôi chỉ cần đổi class path (ví dụ thêm thư viện redis vào), không cần sửa 1 dòng code, không cần phải chơi mấy trò plugin, middleware
zFNuZTA.png


Không biết bên Functional giải quyết như thế nào
BdgiW7R.png
Có một vài điểm mình muốn bạn làm rõ nhé:

Thứ nhất là nếu như việc inject các component vào là tự động và implicit thì điều gì sẽ xảy ra nếu mình quên không inject một component nào đó vào service cần dùng. Nếu mình quên không cấu hình tham số cho một component nào đó thì có vấn đề gì xảy ra hay không. Liệu lúc đó có xảy ra lỗi không hay service sẽ ăn cấu hình mặc định, nếu service xảy ra lỗi thì là lỗi runtime hay lỗi compile time. Nếu thư viện inject class nào đó mà mình không biết thì có vấn đề gì không.

Thứ hai là nếu như mình muốn khởi tạo một service thành nhiều instance và inject các instance đó vào các service riêng biệt thì có được không, khi đó việc khai báo cấu hình riêng cho từng service instance đó sẽ thực hiện như thế nào. Nếu mình muốn khởi tạo một service dynamic at runtime và mình muốn custom các dependency được inject vào service đó thì cách làm là như thế nào.

Còn theo mình hiểu là việc IoC khởi tạo các class khác nhau và inject dependency theo constructor thì chỉ cần ngôn ngữ nào hỗ trợ reflection và annotation là có thể làm được, macro và meta-programming thì FP không thiếu mà còn trang bị full đến tận chân răng.

Còn việc quản lý dependency graph thì cũng không có gì là quá là đặc biệt. Kể cả khi sử dụng thư viện cho IoC bạn vẫn phải khai báo dependency ở đâu đó như hard-code, khai báo ở top-level, đưa ra cấu hình trong config hoặc cấu hình dependency mặc định.

Việc bạn bảo cấu hình classpath gì đó thực ra cũng chỉ là dynamic hay static link, hard code cho dependency và tham số của constructor hay đưa tham số ra cấu hình. Việc bạn phải khai báo annotation chính là một cách để đưa cấu hình dependency ra xml và hoàn toàn không có gì là phức tạp cả.
 
Em không chắc lắm về cú pháp của C# nhưng theo em đọc thì customer được truyền vào constructor. Mỗi instance là 1 customer khác nhau sao mà share customer được
Ừm nhưng mà hai thread share chung một customer cho nên nó không thread safe. Làm ví dụ đơn giản cho bạn dễ hiểu nhé, trong FP code như thế này là side-effect:
Mã:
let a = 1

function getA() {
  a = a + 1;

  return a;
}
Lý do đơn giản thôi là vì nếu vứt hàm này vào hai thread khác nhau là bị dính data race ở a ngay. Và nếu gọi liên tiếp đến khoảng 10 hay 100 lần gì đó thôi là code bắt đầu lỗi. Do đó trong OOP dù class kia bạn đóng gói tốt đến đâu vẫn có thể gây ra side-effect và tạo ra lỗi.

Còn trong FP để một function là pure thì nó không được phép interfere đến bất cứ biến bên ngoài nào cả kể cả input. Còn nếu một hàm đã tạo ra side-effect rồi thì nếu không side-effect theo kiểu này thì nó cũng sẽ side-effect theo kiểu kia. Điều duy nhất mà bạn có thể làm là tạo abstraction đủ cao để tránh side-effect. Còn lựa chọn abstraction như thế nào thì tùy vào side-effect mà bạn muốn xử lý để bạn chọn.

Quay lại với bài kia, nếu như bạn chạy 100 thread mà bạn tạo 100 object khác nhau thì code bạn chạy ok nhưng nếu bạn tạo ra 100 thread mà share chung một object để chạy thì app bạn chết liền. Cách tốt nhất trong bài này là bạn guard lại object được share, khi đó dù bạn chạy 100 hay 1000 thread khác nhau thì ứng dụng của vẫn sẽ ok. Đây cũng chính là ví do vì sao trong FP không cho phép mutate biến trực tiếp mà bắt bạn phải guard biến thông qua một abstraction mới được phép sử dụng.
 
Đây cũng chính là lý do tại sao JS là single thread, vì nếu JS là multi thread giống Go hay C++ thì chắc JS cũng chết từ lâu rồi chứ chưa chắc đã phát triển như bây giờ :3
 
Mình cần ghi rõ lại: IoC Container giải quyết bài toán resolve dependency graph chứ không phải ở việc declare dependency. Bạn có thể declare = xml, annotation, code..., điều đó không quan trọng, cái quan trọng là nó giúp cái đống bạn declare đó và chuyển thành configurated system.
Thứ nhất là nếu như việc inject các component vào là tự động và implicit thì điều gì sẽ xảy ra nếu mình quên không inject một component nào đó vào service cần dùng
Inject là tự động nhưng declare là không tự động. Declare có thể implicit thông qua interface. Còn trường hợp không declare hoặc không xác định được thì nó sẽ fail lúc khởi tạo (runtime) hoặc compile time tùy vào container.
Nếu thư viện inject class nào đó mà mình không biết thì có vấn đề gì không.
Cái này là tùy vào anh declare như thế nào, nếu anh chỉ quan tâm nó implement interface nào thì tất nhiên không vấn đề gì. Đây không phải là triết lý program to an interface, not an implementation sao

Thứ hai là nếu như mình muốn khởi tạo một service thành nhiều instance và inject các instance đó vào các service riêng biệt thì có được không, khi đó việc khai báo cấu hình riêng cho từng service instance đó sẽ thực hiện như thế nào. Nếu mình muốn khởi tạo một service dynamic at runtime và mình muốn custom các dependency được inject vào service đó thì cách làm là như thế nào.
Cái này là 1 chủ đề liên quan đến lifecycle and scope, anh thử dùng 1 cái rồi tự biết. Spring Framework là 1 good starting point.
Nói chung IoC Container không phải là hoàn hảo nhưng nó giải quyết gần hết các vấn đề liên quan đến dependency graph

Còn theo mình hiểu là việc IoC khởi tạo các class khác nhau và inject dependency theo constructor thì chỉ cần ngôn ngữ nào hỗ trợ reflection và annotation là có thể làm được, macro và meta-programming thì FP không thiếu mà còn trang bị full đến tận chân răng
Nói lại: IoC Container là về resolve dependency graph, tôi có bảo làm được hay không, tôi đang nói là không biết bên FP thì không biết có cách nào giải quyết bài toán này không, tôi nghe bảo FP không cần IoC Container nhưng cụ thể thế nào thì chưa thấy nói.

Còn việc quản lý dependency graph thì cũng không có gì là quá là đặc biệt. Kể cả khi sử dụng thư viện cho IoC bạn vẫn phải khai báo dependency ở đâu đó như hard-code, khai báo ở top-level, đưa ra cấu hình trong config hoặc cấu hình dependency mặc định.
Khai báo != Resolve
Việc bạn bảo cấu hình classpath gì đó thực ra cũng chỉ là dynamic hay static link, hard code cho dependency và tham số của constructor hay đưa tham số ra cấu hình. Việc bạn phải khai báo annotation chính là một cách để đưa cấu hình dependency ra xml và hoàn toàn không có gì là phức tạp cả.
Anh chưa xài Spring Boot bao giờ đúng không, tôi đã bảo không sửa bất kì dòng code hay hard code gì nhé, chỉ sửa lại classpath thôi(nghĩa là list các class cần load cho dể hiểu).

Nói cho đơn giản, giả sử tôi có 1 text file như sau:
A->B
B->C
B->D
C->E
C->D
D->E
Thì nhiệm vụ của container chính là build cái graph này từ cái text file:
E, D(E), C(E,D),B(C,D),A(B)
Và tôi đã có thằng A để xài
7pM6OQK.gif
.
 
Sửa lần cuối:
^
Build cái này là cái gì chưa hiểu lắm.

CHo cái ví dụ cụ thể đi

Dependency graph đó thím. Ví dụ như bên trên là class D có dependency là class E (cứ tưởng trượng Controller cần gọi BusinessService hay Repository vậy) thì bên OOP hay dùng là inject từ constructor như này: D(E). Suy nghĩ tương tự cho các class còn lại:
E, D(E), C(E,D),B(C,D),A(B)

Như vậy để có được A thì phải khởi tạo B, C, D, E rồi inject tương ứng vào. Công việc này thường do IoC Container làm 1 cách tự động.
 
Dependency graph đó thím. Ví dụ như bên trên là class D có dependency là class E (cứ tưởng trượng Controller cần gọi BusinessService hay Repository vậy) thì bên OOP hay dùng là inject từ constructor như này: D(E). Suy nghĩ tương tự cho các class còn lại:


Như vậy để có được A thì phải khởi tạo B, C, D, E rồi inject tương ứng vào. Công việc này thường do IoC Container làm 1 cách tự động.

Vậy có vẻ như cái pattern đó sinh ra để giải quyết vấn đề của OOP, functional ko có pattern tương đương. Có thể là mấy cái framework nó sinh ra quá nhiều layer và classes nên mới phải đi giải quyết?
 

Thống kê chủ đề

Ngày tạo
Quynh 123,
Người trả lời cuối
HIL,
Trả lời
593
Lượt xem
97.377
Quay lại
Lên đầu trang