Showing posts with label Abstract Factory. Show all posts
Showing posts with label Abstract Factory. Show all posts

Viết ASP.NET bằng MVP và NHibernate phần 4 - Castle Windsor

Bài viết này được dịch, tóm tắt và bổ sung dựa vào bài viết NHibernate Best Practices with ASP.NET, 1.2nd Ed trên code Project. Các code sample trong bài dựa vào database Northwind của Microsoft và tham khảo 99,99% từ code mẫu của tác giả Billy McCafferty.


I. Giới thiệu Inversion of Control và Castle Windsor


I.1 Giới thiệu


- Có rất nhiều website và blog nói về Inversion of Control và Dependency Injection. Có nguồn cho rằng đây là hai khái niệm khác nhau (MSDN) nhưng sự phụ Martin Fowler thì cho rằng Dependency Injection là tên gọi khác của Inversion of Control để khỏi confused. Tui thì thấy giải thích của bác Martin Fowler khá ổn, dù gì cũng là người lớn tuổi nên chắc không lừa con nít tụi mình :bbpcuoi3:. Trên trang MSDN cúng giải thích khái niệm này rất bình dân nên dân đen như tụi mình rất dễ hiểu::bbpraroi:

Inversion of Control (IoC) có nghĩa rằng các object không phải tự nó tạo ra các objects mà nó phụ thuộc. Thay vào đó, nó sẽ có những objects nó cần nhờ vào một bên thứ ba.

- Còn Inversion of Control Container là một library dựa trên nguyên tắc của IoC để hỗ trợ chúng ta trong việc tạo và hủy các đối tượng trong chương trình. Khi nhắc đến Dependency Injection, ngoài Spring.NET có lẽ người ta sẽ nghĩ đến Castle Windsor. Castle Windsor là một Container mà phần chính của nó là Castle MicroKernel (cũng là 1 container). Theo như Castle Prolect thì Windsor Container là một library dựa trên library MicroKernel nhưng có hỗ trợ thêm khả năng config uyển chuyển và một số tính năng khác. Nói cách khác, Windsor Container tiện dụng hơn MicroKernel và đối với những ai đã sử dụng Spring.NET thì sẽ thấy rằng Windsor cách dùng đơn giản như đang giỡn :bbpraroi:

Castle Windsor

Hình : Lâu đài Windsor ở Anh Quốc

I.2 Cách dùng Windsor


- Trước tiên hãy nghĩ xem Castle Windsor giúp chúng ta giải quyết problem gì. Người ta thường phân chia chương trình thành các module, các layer hay các tầng, tầng này khai báo Interface và tầng kia sẽ có những lớp implement các interface này. Như vậy với cách làm thông thường chắc chắn bằng cách nào đó sẽ có sự phụ thuộc, sẽ có các dòng code đại loại như : IOrderDao orderDao = new NHibernateOrderDao(). Và như vậy rõ ràng nơi nào sử dụng dòng code này đã có sự phụ thuộc vào module/layer chứa lớp NHibernateOrderDao. Người ta muốn tránh sự phụ thuộc này bằng cách thay dòng code có chữ new bằng một cách khác và cách người ta sử dụng chính là Dependency Injection. Có 3 khái niệm Dependency Injection: Constructor injection, Setter Injection và Interface Injection nhưng cái chúng ta thường gặp nhất là Interface Injection. Trong phần này ta sẽ tìm hiểu cách sử dụng Windsor Conainer và Interface Injection.

- Để khỏi phải new 1 object NHibernateOrderDao như trên, ta sẽ sử dụng Castle Windsor. Ta tạo 1 file config tên là Windsor.config rồi add vào project, nội dung của file này như sau:

<?xml version="1.0"?>
<configuration>
<components>
<component id="orderDao"
type="MyProjectName.Data.NHibernateOrderDao, MyProjectName.Data"
service="MyProjectName.Core.DataInterfaces.IOrderDao, MyProjectName.Core">
<parameters>
<nameOfParameter1>value1</nameOfParameter1>
<nameOfParameter2>100</nameOfParameter2>

</parameters>

</component>

</components>

</configuration>

Code 1: Nội dung file config cho Castle Windsor

- Bước thứ hai, mình mở mở file config và thêm vào như sau:

<?xml version="1.0"?>
<configuration>
<configSections>
<section name="castle"
type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

</configSections>
<castle>
<include uri="Windsor.config" />

</castle>

</configuration>

Code 2: Thêm config cho Windsor vào file app/web.config

- Theo như config trên thì file app/web config và file Windsor.config phải ở cùng thư mục với nhau. Trong Code1, ta khai báo một component với id là orderDao (có thể đặt tên gì cũng được), trong đó type và service lần lượt là Interface và class implement tương ứng. Trong phần parameters, ta khai báo các value cần thiết cho constructor của lớp NHibernateOrderDao. Và cuối cùng trong chương trình, ta sẽ dùng code sau đây để lấy được instance của class NHibernateOrderDao như mong muốn:

IWindsorContainer windsorContainer = new WindsorContainer(new XmlInterpreter());
IOrderDao = (IOrderDao)windsorContainer.Resolve("orderDao");
// Or : IOrderDao = (IOrderDao)windsorContainer.Resolve(typeof(IOrderDao));

Code 3: Lấy instance của class NHibernateOrderDao như mong muốn

II. Tạo Web project và cấu hình Windsor, NHibernate


II.1 Load config cho Windsor trong global.asax


- Sau khi tạo một project Web, add file Windsor.config và sửa file web.config như đã nói ở trên xong thì điều tiếp theo ta sẽ nghĩ xem nên load cái config ấy như thế nào. Rõ ràng không nên cứ load config này mỗi lần muốn Resolve 1 object. Tương tự như khi sử dụng log4net cho web application, chúng ta phải load config log4net 1 lần duy nhất và code cho hành động này được đặt ở file global.asax. Nhưng khác với log4net, chúng ta cần một cách nào đó để giữ lại instance của WindsorContainer nên không thể thực hiện trong Global.asax được. Cách của bác Billy rất hay, đó là viết 1 lớp CustomHttpApplication, đặt tất cả các cấu hình cần thiết vào Application_Start, kế đến khai báo một public property để giữ instance của Windsor Container trong suốt thời gian sống của Application và vì thế instance của Windsor Container có thể được sử dụng bất cứ nơi nào trong tầng Web, cụ thể là các Page và các UserControl:

private static IWindsorContainer windsorContainer;
public static IWindsorContainer WindsorContainer
{

get { return windsorContainer; }

}

public void Application_Start(object sender, EventArgs e) {

// Initialize log4net
XmlConfigurator.Configure();

// Create the Windsor Container for IoC.
// Supplying "XmlInterpreter" as the parameter tells Windsor
// to look at web.config for any necessary configuration.

windsorContainer = new WindsorContainer(new XmlInterpreter());

}

public void Application_End(object sender, EventArgs e) {

windsorContainer.Dispose();

}

Code 4: Giữ instance của WindsorContainer trong HttpApplication

II.2 Open Session In View Principle


- Nếu google cụm từ trên thì các bạn sẽ thấy rất nhiều topic viết về chủ đề này trong đó tui thấy nên đọc nhất là trang của Hibernate.org. Vấn đề chúng ta mong muốn giải quyết ở đây là hạn chế số lần NHibernate mở connection đến database. Trong một view bất kì (page/usercontrol) có thể ta sẽ cần load nhiều dữ liệu, gọi nhiều query để load nhiều thông tin khác nhau để hiển thị trên form. Nếu với cách làm bình thường mỗi lần cần 1 Dao object nào đó select dữ liệu ta lại để NHibernate mở một connection thì performance của chương trình sẽ rất thấp, bởi vậy người ta nghĩ ra ý tưởng chỉ mở 1 connection ứng với mỗi View. Kĩ thuật của tác giả là khi có một request đến một view, một connection hay còn gọi là NHibernate session sẽ được tạo ra nếu chưa có, sau khi được sử dụng session NHibernate này sẽ được lưu trong 1 Hashtable, và nếu trong view đó tiếp tục còn Request khác thì session NHibernate này sẽ được lấy ra để tiếp tục sử dụng. Cuối cùng khi không còn Request nào nữa, ứng với sự kiện EndRequest của 1 view thì NHibernate session này sẽ được lấy ra lần cuối để close đi, sau đó empty Hashtable. Vậy nếu trên 1 trang aspx của ta có 3 usercontrol tương ứng 3 view sẽ có 3 lần mở connection đến database mà thôi thay vì mỗi usercontrol bản thân nó lại mở cả đống connection. Vậy làm sao để NHibernate Session chỉ mở một lần ứng với 1 View? Người ta có thể dùng HttpModule để thực hiện mục đích này. :bbpraroi:

- Như chúng ta biết HttpModule được khai báo trong file web.config và cho phép chúng ta implement những function bổ xung cho web application. Một HttpModule sẽ được gọi ngay vào đầu và sau khi có request đến 1 View bất kì. Thông thường người ta sử dụng HttpModule để phục vụ việc Logging hay check Security, trong trường hợp này ta đã implement lớp NHibernateSessionModule(Xem bài 3) để quản lý việc đóng và commit transaction. Lớp này được đặt trong project ProjectBase.Data và Pattern này có thể được sử dụng trong nhiều prolect khác nhau của bạn:

/// <summary>
/// Commits and closes the NHibernate session provided by the supplied <see cref="NHibernateSessionManager"/>.
/// Assumes a transaction was begun at the beginning of the request; but a transaction or session does
/// not *have* to be opened for this to operate successfully.
/// </summary>

private void CommitAndCloseSession(object sender, EventArgs e) {

OpenSessionInViewSection openSessionInViewSection = GetOpenSessionInViewSection();

try {

// Commit every session factory that's holding a transactional session
foreach (SessionFactoryElement sessionFactorySettings in openSessionInViewSection.SessionFactories) {

if (sessionFactorySettings.IsTransactional) {

NHibernateSessionManager.Instance.CommitTransactionOn(sessionFactorySettings.FactoryConfigPath);

}

}

}
finally {

// No matter what happens, make sure all the sessions get closed
foreach (SessionFactoryElement sessionFactorySettings in openSessionInViewSection.SessionFactories) {

NHibernateSessionManager.Instance.CloseSessionOn(sessionFactorySettings.FactoryConfigPath);

}

}

}

Code 5: Implement 1 lớp HttpModule để apply Open In View Principle

- Và đây là một phần của file web.config để khai báo HttpModule này:

<system.web>
<compilation debug="true" />
<httpModules>
<add name="NHibernateSessionModule" type="ProjectBase.Data.NHibernateSessionModule, ProjectBase.Data" />

</httpModules>

</system.web>

Code 6: Khai báo HttpModule trong web.config

III. Cải tiến DaoFactory bằng Generic và Castle Windsor



- Hiện tại có một vấn đề với DaoFactory của chúng ta. Khi cần tạo mới một class XXXDao nào đó cho một entity mới, chúng ta phải mở code, sửa lại interface IDaoFactory và thêm vào một dòng IXXXDao GetXXXDao(). Tất nhiên chúng ta cũng phải sửa lại lớp NHibernateDaoFactory, implement thêm method GetXXXDao(). Như vậy khá là bất tiện. Bác huynguyen_fisherman (đồng nghiệp, PM của project NHAU:bbpcheer:) đã đề nghị tui cách làm khác như sau:

1/ Refactoring lại interface IDao, thay tên của nó thành IGenericDao:

Castle Windsor

Hình : Refactor lại interface IDao

2/ Thêm vào một interface rỗng IDao và để IGeneric Dao inherit từ interface IDao này

3/ Sửa lại interface IDaoFactory như sau:

public interface IDaoFactory
{

T GetDao(string id) where T : IDao;

}

Code 7: Sửa lại interface IDaoFactory

4/ Implement lại class NHibernateDaoFactory:

public class NHibernateDaoFactory : IDaoFactory
{

public NHibernateDaoFactory()
{
}

// id is "component id" that we "declare" in the CastleComponents.config
public T GetDao(string id) where T : IDao
{

Check.Require(string.IsNullOrEmpty(id) == false, "component id cannot be null or empty");
return (T)_windsorContainer.Resolve(typeof(T));

}

private IWindsorContainer _windsorContainer = new WindsorContainer(new XmlInterpreter());

}

Code 8: Sửa lại class NHibernateDaoFactory

5/ Và đây là cách sử dụng để lấy 1 Dao object như ý muốn :bbpxtay:

IDaoFactory daoFactory = new NHibernateDaoFactory();
ICustomerDao customerDao = daoFactory.GetDao<ICustomerDao>("CustomerDao");

Code 9: Cách lấy một Dao bằng NHibernateDaoFactory

IV. Tóm tắt & Kết luận


- Castle Windsor là một IOC Container rất được ưa thích, nếu các bạn xem ở blog này http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx sẽ thấy rằng Windsor xếp đầu bảng và ngoài nó ra còn rất nhiều library Dependcy Injection nữa. Trong số đó thì có Unity và NInjection là hai IoC Container tui muốn tìm hiểu khi có thời gian :D.

- Trong bài này ta đã tìm hiểu cách áp dụng Generic và IoC Container để làm cho code gọn gàng, dễ sửa đổi. Chúng ta chỉ mới tạo project web và chuẩn bị trước một số thứ như config NHibernate và Windsor cũng như làm quen với Open In View Principle. Trong phần tiếp theo chúng ta sẽ tìm hiểu cách áp dụng pattern MVP để làm các trang aspx. Cách làm mới có gì khác so với cách làm truyền thống? Hãy chờ hồi sau sẽ rõ :bbpraroi:



Code phần 4: http://nthoaiblog.googlepages.com/EnterpriseSample-part4.zip
Các đoạn code minh họa trong bài viết được tui rút gọn cho dễ hiểu, code được implement cuối cùng trong demo sẽ có nhiều điểm khác biệt...
(Còn tiếp)

Phỏng vấn 2 mẫu thiết kế Abstract Factory và Factory Pattern

Interview Abstract Factory and Factory Method (Design Patterns)



Nguyễn Thoại
: Chà chà, chào hai anh, đây là lần đầu tiên blog của iem phỏng vấn hai anh, hai anh cảm thấy thế nào.

Factory Method: Tôi ko biết sao hay bị gộp chúng với cha Abstract Factory. Đều là factory pattern không có nghĩa chúng tôi không được có những buổi phỏng vấn riêng.:bbpcaycu:

Nguyễn Thoại: Ấy ấy, đừng tự ái, em muốn phỏng vấn cả hai anh cùng nhau để giúp đọc giả phân biệt được ai là ai. Hai anh có nhiều điểm giống nhau, và em có nghe vài phàn nàn rằng người ta thỉnh thoảng bị rối và không thể phân biệt được 2 người.

Abstract Factory: Ừ đúng đấy, đã có lúc tôi bị nhầm lẫn là cha Factory Method, và tôi biết ổng cũng bị giống như tôi. Hai tụi tôi đều giỏi trong việc giảm sự phụ thuộc giữa chương trình với những cài đặt cụ thể; nhưng mỗi người chúng tôi lại có những cách làm riêng nên thỉnh thoảng dân lập trình lại cảm thấy rối và không thể xác định được phải dùng cách nào.:bbpcuoi3:

Factory Method: Ấy, người ta vẫn phân biệt được tôi mà. Nói chung, tôi dùng các lớp để tạo ra products, còn cha dùng những đối tượng; hai cách đó hoàn toàn khác nhau nhé.

Nguyễn Thoại: Ừ em có tìm hiểu về hai anh rồi mà vẫn còn rối đây này, anh có thể nói thêm về khoản này không, anh Factory Method?:big_smile:

Factory Method: Ok, Cả cha Abstract Factory và tôi đều tạo ra các products, các objects, đó là việc chúng tôi phải làm. Nhưng tôi làm nhờ vào sự kế thừa (inheritance) …

Abstract Factory: … còn tôi làm nhờ vào sự kết hợp các đối tượng.

Factory Method: Đúng thía, nghĩa là nếu muốn tạo ra các đối tượng bằng cách Factory Method, người ta cần phải extend một lớp và override lại hàm tạo “factory method”.

Nguyễn Thoại: …rồi factory method sẽ làm cái gì?

Factory Method: Tất nhiên là tạo ra 1 object roài, anh đã từng viết bài về tôi mà giờ còn hỏi hả. Ý tưởng của tôi, Factory Method Pattern, là sẽ sử dụng các lớp con để sinh ra 1 đối tượng mong muốn. Bằng cách đó, người dùng sẽ chỉ cần biết đến lớp trừu tượng như gia cầm, và các lớp con cụ thể sẽ lo về các kiểu gà, kiểu vịt, kiểu ngan. Vì vậy, nói theo cách khác, tôi giúp chương trình độc lập với các kiểu (type) cụ thể đó.:bbpskien:

Abstract Factory: Ý tưởng của tôi cũng giống giống vậy đó, nhưng chỉ là làm theo một cách khác…

Nguyễn Thoại: Tiếp đi anh Abstract Factory … vừa rồi anh có đề cập đến cách kết hợp các đối tượng?

Abstract Factory: Ờ,cách của tôi thế này: Tôi tạo ra một kiểu trừu tượng (abstract type) để dùng vào việc tạo ra một nhóm những products khác. Khi đó, những lớp con của kiểu trừu tượng sẽ xác định cách tạo ra các products này. Để áp dụng được ý tưởng của tôi, bạn phải tạo ra một instance của một trong các lớp con trên (instance này là 1 factory) và đưa nó vào chỗ cần thiết trong code. Vì thế, giống như Factory Method, những nơi sử dụng factory của tôi sẽ hoàn toàn độc lập với những produtcts cụ thể.

Nguyễn Thoại: Oh yè, tôi biết rồi, một lợi ích nữa của cách này là anh đã nhóm các products tương tự nhau lại, có đúng không?

Abstract Factory: Đúng roài cưng à.:bbpchao:

Nguyễn Thoại: Bây giờ người ta cần bổ sung thêm một product nữa vô nhóm các products mà anh có thể tạo ra. Vậy không cần phải thay đổi gì hết huh?

Abstract Factory: Hèm, trường hợp này thì tôi phải thay đổi chút ít, nhiều người không thích lắm ở điểm này.

Factory Method: hĩ hĩ:bbpcuoi3:

Abstract Factory: Cười gì cha nội?:bbpbuon:

Factory Method: Cười chứ sao ko cười, làm vậy quá mất công chứ gì nữa. Cha thay đổi có nghĩa là người dùng phải đi mà đổi tất cả các lớp con roài? (các lớp con ở đây là các factories)

Abstract Factory: Đúng, nhưng cần phải làm như vậy vì tôi có khả năng tạo ra nhiều kiểu product khác nhau, trong khi người ta xài cách của anh khi họ cần tạo ra một kiểu product nào đó thôi, vì thế anh không có cồng kềnh như tôi, anh chỉ cần một method duy nhất.:bbpnodo:

Nguyễn Thoại: Anh Abstract Factory bớt nóng, tôi nghe đồn rằng anh thường sử dụng nhiều hàm factory method theo cách của anh Factory Method để tạo các đổi tượng bên trong những factories của anh, điều đó có đúng ko?

Abstract Factory: Đúng đó em, anh thừa nhận điêu này. Những lớp factory con thường dùng các factory method để tạo các products tương ứng. Trong trường hợp này, các factory method được dùng thuần túy để tạo ra các products.

Factory Method: … còn tôi dùng hàm factory method để tạo ra product cụ thể mà người ta muốn, người dùng sẽ không biết cái gì được tạo ra, họ chỉ cần gọi hàm.

Nguyễn Thoại: Dường như hai anh làm rất tốt công việc của mình. Tôi chắc rằng đọc giả đã có được lựa chọn của mình. Sau cùng, những factories của 2 anh quả thật rất hữu ích, đọc giả của tôi sẽ sử dụng chúng trong các tình huống thích hợp nhất. Các anh đều đóng gói (encapsulate) quá trình tạo ra đối tượng để giúp chương trình độc lập và giảm phụ thuộc với những kiểu cụ thể, việc làm này thật đáng tuyên dương. Cám ơn hai anh, hai anh có lời gì trước khi chào tạm biệt đọc giả không ạ?:bbpbtay:

Abstract Factory: Cám ơn. Hãy nhớ đến tôi, Abstract Factory, và sử dụng tôi bất cứ khi nào bạn cần cùng một lúc tạo ra nhiều loại products, và khi bạn muốn chắc chắn những nơi sử dụng sẽ không cần biết đến những lớp cụ thể khi cần làm việc này.:bbpskien:

Factory Method: Còn tôi là Factory Method, hãy sử dụng tôi để làm cho chương trình của bạn độc lập với những lớp cụ thể mà bạn cần tạo 1 đối tượng, hoặc khi bạn không biết sau này sẽ cần đến những lớp con nào nữa. Khi cần sử dụng tôi, hãy tạo tạo ra subclass (1 factory implement 1 kiểu abstract) và implement factory method cho chính bạn.:bbpraroi:



(Page 158-162 of Head First – Design Pattern)



Abstract Factory and Factory Method (Design Patterns)





Dependency Injection with Spring.Net p1

(Bài viết dịch từ bài Dependency Injection with Spring.Net của David Consdorf, thêm hành thêm tiêu rồi nấu lại bởi Nguyễn Thoại :bbpraroi:)



Introduction:

- Spring.Net là một framework hỗ trợ nhiều chức năng cho những ứng dụng .NET. Phần nhân chính của Spring.Net là Dependency Injection hay còn được biết đến với tên gọi Inversion of Control(IoC). Tôi có bổ sung đoạn giới thiệu vui về 2 mẫu design pattern Factory. Ngoài ra, với những đoạn code mẫu mà tác giả cung cấp (ở các phần sau), hi vọng các bạn sẽ có một cái nhìn cơ bản về Spring.Net và có thể áp dụng nó cho những ứng dụng của mình.

Spring.Net: Overview

- Spring.Net là một framework nguồn mở cung cấp một tập các components giúp người lập trình áp dụng được những kĩ thuật nổi tiếng như Dependency Injection và Aspect-Oriented Programming. Bộ Spring.Net được chuyển từ phiên bản Java của Spring framework. Ngày nay, phiên bản Java của Spring đã được sử dụng rất thành công trong rất nhiều ứng dụng. Cùng với sự thành công của người anh em Spring Java, sự xuất thiện của Spring.Net chỉ là vấn đề thời gian. Thực sự như vậy, kể từ khi ra đời, Spring.Net đang chiếm được sự ủng hộ nhiệt tình của cộng đồng .NET

- Các chức năng của Spring.NET:

  • Dependency Injection
  • Aspect-Oriented Programming
  • Data access support including support for NHibenrate (ORM framework)
  • Web Framework
  • Integration Testing
  • Validation Framework
  • Dynamic Reflection
  • Threading and Concurrency Support
  • Object Pooling
  • Expression Evaluation
  • Services
  • Và nhiều chức năng khác...

- Cùng với sự đóng góp của cộng đồng, Spring.Net đang dần phát triển với nhiều chức năng mới đang được bổ sung thêm. Loạt bài viết này chỉ hi vọng truyền tải được chức năng thứ nhất cho các bạn. Chúng ta cũng cần lưu ý rằng Spring.Net cũng như một chiếc hộp gồm nhiều công cụ trong đó. Bạn không cần thiết phải sử dụng hết mọi thứ trong chiếc hộp này. Chúng ta có thể kết hợp một số chức năng và sử dụng những gì cần thiết cho ứng dụng của mình. Ngoài ra, Spring.Net còn được chia ra thành nhiều assemblies riêng và chúng ta không cần phải nhúng toàn bộ framework Spring vào chương trình.

Dependency Injection with a Factory Pattern

- Thành phần chính của Spring.Net là Dependency Injection(DI) còn được biết đến như Inversion of Control (IoC). Ý tưởng cơ bản của Dependency Injection là làm giảm sự phụ thuộc của những đối tượng (Objects) trong hệ thống. Dependency Injection cho người lập trình khả năng thiết kế những chương trình, trong đó các đối tượng (Objects) có thể tự thiết lập mối quan hệ đến những đối tượng khác mà nó cần. Thậm chí cho phép những tiến trình (processes) có sử dụng đối tượng làm công việc này. Mục tiêu chính của DI là hướng tới tính Loose Coupling, vì vậy nhóm những đối tượng (objects) nên làm việc độc lập với nhau, tập trung trên những behavior của chúng mà không cần biết những nhóm đối tượng khác (reference tới) sẽ làm việc như thế nào cũng như được thiết lập (construct) như thế nào.

- Để hiểu kĩ hơn về khái niệm Dependency Injection, chúng ta sẽ nhìn lại những thiết kế truyền thống của DI bằng cách sử dụng các lớp factory. Được mô tả kĩ trong các tài liệu Design Patternc có 2 mẫu thiết kế factory: Abstract Factory Pattern và Factory Method Pattern. Xin được sử dụng câu chuyện sau để giải thích cho dễ hiểu:


- Thị trường chứng khoán Việt Nam những tháng đầu năm 2008 vô cùng ảm đạm, tôi thấy mình đua đòi đầu tư chứng khoán vô những ngành bất động sản, tài chính không ổn chút nào. Trong khi đó tình hình lương thực thực phẩm trên thế giới vô cùng HOT. Sau nhiều ngày suy nghĩ,:bbpnghi:tôi quyết định đổ tiền đầu tư vô ngành thực phẩm bánh kẹo .Tôi sẽ mở một cơ sở sản xuất bánh. Trong nhóm sản phẩm bánh mì, tôi quyết định đầu tư làm bánh hambơgơ và bánh mì Sài Gòn đặc ruột thơm bơ :bbpraroi:. Như vậy phải mua máy để làm bánh, tôi vay họ hàng mua 2 cái máy sẽ làm ra 2 loại bánh trên, một máy chuyên để làm bánh mì Sài Gòn và một máy chuyên làm ra bánh hambơgơ, nhà máy của tôi sẽ thay phiên bật 2 máy đó vào các ngày chẳn và lẽ trong tuần để làm bánh, ngày nào cần làm bánh loại nào thì bật máy loại đó. Và sơ đồ nhà máy tôi như sau:


Abstract Factory Method

Hình 1: Abstract Factory Pattern

Bản thiết kế:



abstract class MayLamBanh
{
public static MayLamBanh ChonMayLamBanh()
{
bool ngayChan = KiemTraNgayChanLe();
if (ngayChan == true)
{
return new MayLamBanhhamburger();
}
else
{
return new MayLamBanhMiSaiGon();
}
}
public abstract Banh LamBanh();
}

class MayLamBanhhamburger : MayLamBanh {
public override Banh LamBanh() {
return new BanhHamburger();
}
}

class MayLamBanhMiSaiGon : MayLamBanh {
public override Banh LamBanh()
{
return new BanhMiSaiGon();
}
}

abstract class Banh {
}

class BanhHamburger : Banh {
}

class BanhMiSaiGon : Banh {
}

public class NhaMay {
public static void main(String[] args) {
MayLamBanh factory = MayLamBanh.ChonMayLamBanh();
Banh banh = factory.LamBanh();
}
}

- Như vậy khi áp dụng Abstract Factory Pattern là bạn tạo ra những lớp CFactory, tất cả những CFactory này sẽ implement cùng 1 interface IFactory, khi đó instance factory thuộc kiểu nào sẽ làm ra product tương ứng.

- Quay lại chuyện làm bánh, sau một thời gian tôi kiểm toán sổ sách và thấy rằng tiền lời không nhiều. Chi phí quản lý và bảo trì hai cái máy quá tốn kém trong khi đối thủ cạnh tranh thì ngày càng tạo ra sản phẩm đa dạng. Giờ nếu muốn làm ra sản phẩm bánh kem để cạnh tranh thì phải mua thêm máy khác vừa tốn kém lại tốn chi phí bảo trì. :bbpbuon: Tôi đọc báo Dân Trí và phát hiện rằng các chú kĩ sư cơ khí Việt Nam vừa chế ra một cái máy đa năng có thể tạo ra đủ loại bánh. Chỉ cần thêm vào công thức vào là máy có thể làm ra bánh theo ý muốn. Khi đó tuỳ tỉ lệ đường, tỉ lệ bột, tỉ lệ bột nở, tỉ lệ nước mà sẽ tạo ra loại bánh theo công thức. Tôi khoái quá, bán rẻ ngay 2 cái máy dỏm và vay thêm chút đỉnh mua cái máy đa năng đó về. Các chú kĩ sư cơ khí giao cho tôi bản thiết kế mới như sau:


Abstract Method Pattern

Hình 2: Factory Method Pattern

Bản thiết kế:


class MayLamBanhDaNang : MayLamBanh
{
public Banh LamBanh(double soLuongDuong, double soLuongBot, double soLitNuoc)
{
if (soLuongDuong / soLuongBot){
return new BanhHamburger();
}
else
{
return new BanhMiSaiGon();
}
}
}


- Nhờ và cái máy này, xưởng sản xuất của tôi ăn nên làm ra và chuẩn bị lên đời thành công ty cổ phần bánh kẹo nước giải khát BSG (Bánh Mỳ Sài Gòn) :bbpxtay:

- Như vậy Factory Method Pattern là mấu thiết kế mà bạn tạo ra một interface IFactory có khai báo hàm tạo ra những product (Object). Sau đó bạn để trách nhiệm tạo ra product loại nào cho lớp CFactory implement IFactory của bạn.

Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.

- Tóm lại, factory là một hoặc nhiều đối tượng chịu trách nhiệm tạo ra những đối tượng khác mà chương trình sử dụng. Nói chung, ứng dụng sẽ không biết gì về cách tạo ra những đối tượng nó cần, thay vào đó nó chỉ nên biết đến những đối tượng đó thông qua 1 interface. Bằng cách đóng gói (encapsulation) những cài đặt (implementation) và thiết lập (construct) cho lớp trong factory, chương trình của chúng ta có thể sử dụng những gì nó cần mà không biết đối tượng cụ thể nào đang được gọi, đối tượng đó được tạo ra như thể nào cũng như những đối tượng đó làm gì.

- Trong những ứng dụng lớn với khả năng hỗ trợ nhiều thiết lập, cấu hình, factories còn có thể đóng vai trò như bộ phận trung tâm thực hiện việt thay đổi những cấu hình đó. Ví dụ, nếu bạn cần chuyển đổi nhiều database component truy xuất dữ liệu MySQL và thay thế chúng bằng những component mới truy xuất dữ liệu Oracle, tất cả những gì bạn cần làm là đổi lớp factory và chuyển nó sang những component Oracle. Phần còn lại của ứng dụng sẽ không bị ảnh hưởng, bởi vì tất cả những component database đều được cài đặt (implement) từ cùng 1 interface, gồm các hàm truy xuất, lưu cũng như cập nhật và xoá dữ liệu. Còn làm cách nào mà những component đó làm việc bên dưới và loại database nào mà chúng truy xuất thực sự không quan trọng.

-----------------------------------------------------------------------------------

:feel_good: Vậy trong cái nhà máy làm bánh củ chuối ở trên thì DI thể hiện ở chỗ nào? Dependency Injection ở điểm sau: thời còn 2 cái máy làm 2 loại bánh, tôi thích làm ra loại nào thì tôi xài cái máy ấy. Bây giờ bánh mì SàiGòn đặc ruột thơm bơ của tôi ế ẩm vì không cạnh tranh nổi với mấy người làm bánh mì bán dạo bằng xe đạp. Cho nên tôi muốn chuyển qua sản xuất thêm bánh bao. Tôi có thể thuê hoặc mua mới một cái máy làm bánh bao tự động về để ở một chỗ trong xưởng mà không cần phải đập xây lại nhà máy sản xuất, không cần tổ chức lại khâu đóng hộp phân phối. Tôi cũng chẳng cần phải bán đi cái máy làm bánh mì Sài Gòn vì biết đâu tương lai mấy cha bán bánh mì dạo đổi sang bán keo dính duột, lúc đấy thì bật lại máy làm bánh mì để đa dạng sản xuất. Nhưng kinh doanh quả là không đơn giản với những người hiền lành, nghe lời mấy bạn kĩ sư cơ khí mua cái máy đa năng làm bánh tôi mới ôm hận. Tuy cái máy đó làm được nhiều loại bánh nhưng tốc độ làm bánh vừa chậm lại còn hao điện, ngoài ra lượng hao hụt nguyên vật liệu quá lớn khiến giá thành sản phẩm quá cao không cạnh tranh nổi với Kinh Đô Bakery. Tôi lại mang tính chủ quan, nghĩ rằng với một cái máy này là đủ nên không có máy dự phòng. Bây giờ tôi muốn thay máy khác cũng phải đợi đến lúc có máy thì mới đưa vào sử dụng chứ không thể ngừng sản xuất. :bbpcuoi3:Tôi quyết định liều thuê đám kĩ sư chất lượng cao vừa tốt nghiệp đại học làm riêng cho tôi một cái máy dựa trên bản thiết kế ban đầu, làm sao cho ít hao điện và nguyên vật liệu, tốc độ làm bánh nhanh hơn. 1 tháng sau đó tôi đã có được cái máy mới như mong muốn, tôi thuê xe ôm chở máy vào xưởng đặt một chỗ thoáng mát, bật điện máy mới và shut down máy cũ. Thế là dây chuyền làm bánh của tôi chạy liên tục chỉ mất một chút thời gian bật tắt mà không phải ngừng sản xuất trong thời gian dài. Tuy vậy hệ thống của tôi vẫn có chút bất tiện. Như các bạn thấy lúc mới mở phân xưởng, tôi sử dụng 2 cái máy để làm bánh. Lúc cần làm bánh hambơgơ tôi phải vào phân sưởng tắt điện máy làm bánh mì và bật điện máy kia. Đến khi có cái máy đa năng, thình thoảng lại phải chạy đến xưởng để đổi công thức làm bánh. Thật là phiền khi chạy đi chạy lại, ngừng toàn nhà máy để thay đổi. Thật may lúc trước ở công ty cũ tôi có quen một tay rất rành về hệ thống mạng điều khiển từ xa, hắn tư vấn cho tôi một hệ thống gọi là Spring cài đặt nối mạng máy làm bánh với hệ thống máy tính, tôi chỉ cần ngồi ở nhà click chuột là nhà máy của tôi sẽ làm ra bánh khác mà không cần phải ngừng hoạt động hay khởi động lại dây chuyền hoặc chạy vô phân xưởng để đổi công thức. Bị lừa lần trước tôi không tin tưởng nên lên mạng google vài cú, và tôi tìm được thông tin rằng mấy tay sản xuất kẹo dừa bên Java Indonesia đã sử dụng hệ thống Spring bên đấy mấy năm nay rồi và rất thành công.


Phần 2

Phần 3

Phần 4

Phần 5

Phần 6

(Các bạn đang xem phần 1)

rss
 

About Me

Place I've live
Near Bossley Park, Sydney, NSW, Australia
Place I've work
  • Freelancer (from 06/2010 to present)
  • Harvey Nash (from 05/2008 to 06/2010)
  • DataDesign Vietnam (10/2005 to 04/2008)
Place I've studied
  • University of Natural Science (Bachelor of Science HoChiMinh City Vietnam From 2001 to 2005)
  • Le Hong Phong High School (HoChiMinh City Vietnam From 1997 to 2000)