• Thứ Hai, 26/06/2006 10:00 (GMT+7)

    Viết phần mềm trong 15 phút

    Viết bài này mình cứ sợ "múa rìu" qua mắt các chuyên gia lập trình. Tuy nhiên, qua thực tế đi dạy, thấy những bạn sinh viên mới ra trường biết rất ít về thiết kế phần mềm, nên mạn phép dùng ngôn ngữ "cây nhà lá vườn" để giúp các bạn mới vào nghề có được khái niệm về cấu trúc cơ bản của một phần mềm và vận dụng để cải thiện năng suất lập trình.

    Thành phần

    Việc thiết kế và phát triển một phần mềm tiêu tốn rất nhiều tài nguyên, nhưng sau đó thường không sử dụng lại được. Ví dụ, khi viết chương trình quản lý kho, bạn phải xử lý SQL, tạo form, tạo report, kiểm tra bảo mật... Sau đó, có khách hàng yêu cầu viết chương trình kế toán thì bạn phải viết lại những chức năng tương tự.

    Bạn nghĩ sao nếu như thiết kế một phần mềm mới cũng đơn giản như lắp một cái tivi? Chẳng hạn như bạn chỉ việc gắn bộ nguồn, mạch bắt sóng, mạch điều khiển, gắn đèn hình vào là xong. Bạn không cần phải đi thiết kế lại từng phần chi tiết tỉ mỉ làm gì cả. Giả sử bạn xoay sang lắp máy tính xách tay thì cũng thế, chỉ việc gắn bộ nguồn, đèn hình, mạch điều khiển. Điểm hay là ở chỗ một con transistor trong laptop hay tivi thì cũng y như nhau.

    Quay lại ví dụ viết chương trình quản lý kho, giả sử ta có một máy tính siêu thông minh thì chỉ việc bảo nó: gắn "cục" Security A101, cục Data 2.0, cục Web GUI 8.1 rồi dán nhãn My Big Soft vào đó rồi nó tự động làm hết mọi chuyện cho ta. Rất tiếc, đây chỉ là ước mơ, còn thực tế thì lập trình viên vẫn phải còng lưng viết code đến mờ mắt, viết đi viết lại, viết tới viết lui như một điệp khúc bất tận.

    May thay, thành phần phần mềm (component) có thể giải quyết vấn đề. Nếu bạn là dân Java, hãy nghĩ đến Java Beans. Nếu bạn là dân .NET, hãy nghĩ đến Application Block, đến Web-parts. Hay đơn giản hơn, ai cũng đã gặp nhiều lần: UI control (button, label, listbox, checkbox,...)

    Một component không phải là một lớp (class), và lập trình thành phần (component-centric) cũng không phải là lập trình hướng đối tượng (OOP - Object Oriented Programming). Class đơn thuần chỉ là gom nhiều code có cùng mục đích vào chung một chỗ. OOP là xem vấn đề như một hoặc nhiều đối tượng (có thuộc tính, có method) để phân loại mối quan hệ của chúng. Còn component-centric có nghĩa là lập trình để mỗi phần mang tính độc lập, có thể thay thế, có thể sử dụng lại cho những vấn đề khác nhau.

    Giả sử bây giờ bạn phải viết trò chơi Snake (người dùng điều khiển con rắn chạy ăn mồi, mỗi khi ăn được cục mồi thì con rắn dài thêm một đoạn).

    1/ Class: bạn chỉ cần 1, cùng lắm là 2 class để viết trò chơi tí hon này.

    2/ OOP: bạn sẽ viết các class Snake, Food, Player.

    3/ Component-centric: Bạn sẽ ngồi phân tích xem đâu là điểm chung, đâu là điểm riêng, đâu là phần chi tiết chỉ áp dụng riêng cho trò chơi này, đâu là phần bạn có thể abstract nó. Có lẽ bạn sẽ thiết kế ra các component sau: Game Engine, Graphic Engine, Rule Engine, Resouces Manager, User Controller, v.v...

    Như vậy sau khi thiết kế xong, trò Snake chỉ là sản phẩm phụ mà thôi. Với những component sẵn có, bạn dư sức viết DOOM 2006.

    Phát triển thành phần phần mềm đang được đầu tư và phát triển rất nhiều. Nếu bạn có hứng thú, hãy tham khảo thêm các tài liệu sau: Microsoft Application Block; Enterprise Java Bean; Java Frameworks and Components: Accelerate Your Web Application Development – Michael Nash...

    Lớp

    Nếu như component là từng bộ phận nhỏ, đóng vai trò như một hộp đen "black-box", ta chỉ quan tâm tới chức năng của nó là chính, thì lớp (Layer) lại giống như một bản mạch in gồm nhiều component đã được thiết kế sẵn. Lấy ví dụ như card màn hình, mở các máy PC ra bạn sẽ thấy ngay card này. Điểm thú vị là bạn không phải "se duyên" với cái card ấy mãi mãi. Khi nào túi tiền rủng rỉnh, bạn có thể mua card khác mới hơn, nhanh hơn, xịn hơn để gắn vào và quên béng đi cái card cũ. Có khi nào bạn suy nghĩ lại và ngạc nhiên tại sao cái máy tính cũ kỹ đời 1998 của mình lại có khả năng chấp nhận card 3D đời 2006 không? Thật là một điều kỳ diệu, nhỉ!

    Phần mềm cũng thế, nếu thiết kế chia một phần mềm ra thành nhiều layer thì sẽ tăng tính tái sử dụng, và quan trọng nhất là: chịu được sự thay đổi trong tương lai. Bạn hãy nghĩ thế này nhé: nếu Windows mà được thiết kế tốt hơn thì bạn đã có thể chơi game của Windows, chạy web server của Linux, và chạy chương trình đồ họa của Macintosh ngay trong hệ điều hành Windows.

    Ở đây tôi giới thiệu 3 layer cơ bản nhất mà đa số chương trình từ bé đến khổng lồ, từ bài tập của sinh viên đến game online kinh phí hàng trăm triệu đô đều cần phải có.

    Data Access Layer (DAL)

    Nếu bạn biết "Select * from Products Where CustID = @ID" nghĩa là gì nhưng không cần phải dùng mỗi ngày thì bạn may mắn quá, bạn có thể bỏ qua phần này.

    Nếu bạn không những biết mà còn thuộc nằm lòng đến 80% T-SQL 92, hoặc bạn nằm mơ cũng nghĩ đến SQL, đến Stored Procedure, đến Triggers, thì bạn rất cần phải dùng đến DAL. Có lẽ 90% dân lập trình, nhất là ở Việt Nam trong thời điểm hiện tại, rơi vào trường hợp này.

    Trước hết, hãy nói Data Access Object là gì đã. Khi lập trình cơ sở dữ liệu, bạn phải lặp đi lặp lại thao tác sau:

    - Create connection

    - Create SQL command

    - Execute SQL

    - Process results

    Chán quá, lỗi nhiều quá. CSDL bạn dùng là quan hệ (relational), mọi thứ đều trong table, table, table. Trong khi đó, bạn lại thích lập trình OOP cơ. Thế là bằng cách này hay cách khác, cho dù bạn biết hay không biết, bạn sẽ quay sang làm theo kiểu sau: định nghĩa class chuyên nói chuyện với CSDL. Lấy ví dụ như class sau:

    Class ProductDAO

    { Connection GetConnection();

    bool Insert(int ID, string Name);

    DataSet GetAllProducts();

    DataSet GetProductByName(string Name);

    Bool Delete(int ID);

    }

    Phương pháp bạn vừa làm chính là Data Access Object. Bạn có thể viết tay, bạn cũng có thể dùng các công cụ như CodeSmith để làm giùm bạn. Xin chúc mừng! Bạn đã đỡ khổ hơn trước nhiều rồi đấy.

    Nhưng mà, cũng xin... chia buồn với bạn luôn. Bạn nghĩ sao nếu CSDL bên dưới thay đổi? Bạn sẽ dùng CodeSmith để tạo lại ư? Thế mấy cái "business logic" (luận lý nghiệp vụ) đi tong hết thì sao? Lỡ năm sau CodeSmith dẹp tiệm thì sao, bạn phải sửa lại bằng tay à? Hoặc là CSDL không phải của bạn, mà bạn phải tích hợp vào CSDL "bự xự" có sẵn của khách hàng? Chua đấy bạn ạ. Chưa kể là dùng Data Access Object làm tăng số lượng class lên rất nhiều (cứ mỗi table trong CSDL cần ít nhất 1 class, thậm chí có thể là 3, 4 class). Mỗi class cần ít nhất 4 method (Create, Read, Update, Delete). Chưa kể là mỗi kiểu select khác nhau lại phải viết method mới. Điều này đồng nghĩa với việc kiểm thử (testing) cũng tăng lên đến chóng mặt.

    Bạn nghĩ sao nếu bạn chỉ cần định nghĩa một class thế này:

    Class Product

    { Int ID;

    String Name;

    String Description;

    }

    Xong, chỉ có thế thôi! Nếu cần thêm sản phẩm mới vào database thì làm như sau:

    Product p = new Product();

    p.Name = " Some product";

    Database.AddNew(p);

    Nếu cần truy vấn một sản phẩm thì chỉ cần thế này:

    Product p = Database.Get(typeof(Product), Name = "ProductA");

    Rất đơn giản, phải không bạn? Cái hay là ở chỗ nếu có thêm nhiều table nữa thì cũng thế, bạn chả phải viết thêm nhiều method chi cho mệt, chỉ định nghĩa class của bạn ở mức đơn giản nhất. Và "đã” nhất là bạn không cần phải viết thêm một mớ test để kiểm tra việc truy xuất class đó.

    Đây chính là chức năng chính của Data Access Layer.

    Nếu thích, bạn có thể tham khảo các tài liệu sau: Data Access Layer trong Microsoft Application Block; O/R Mapping (Object-to-Relational Mapping): Wilson O/R for .NET, ORM.Net, Object Space; Java Persistence for Relational Databases, Richard Sperko (Apress 2003- ISBN:1590590716).

    Lưu ý: Persistence Layer về cơ bản có cùng tính năng như DAL. Tuy nhiên, Persistence Layer có khái niệm và cách thức thực hiện khác với DAL một ít, mỗi loại có cái hay và cái dở riêng.

    Business Object Layer (BOL)

    Business Object (đối tượng nghiệp vụ) rất thú vị ở chỗ chương trình nào cũng cần có nó, nhưng lại chẳng có framework hay chuẩn nào cho bạn cả. Đơn giản là vì business object thay đổi luôn tùy yêu cầu cụ thể của từng nghiệp vụ (business) khác nhau.

    Trong đa số trường hợp, Business Object sẽ được thiết kế gần giống với Data Object (chỉ chứa dữ liệu hoặc nói chuyện với CSDL), chỉ khác ở chỗ thêm vào đó một ít quy tắc kiểm tra nghiệp vụ (ví dụ: nếu tài khoản chỉ có 1000 thì không cho phép rút 1 triệu đồng).

    Tuy nhiên, có những vấn đề lặp đi lặp lại mà nghiệp vụ nào cũng gặp, chẳng hạn: transaction (giao tác), distribution (phân phối), validation (kiểm tra). Khi thiết kế Business Object, người thiết kế bao giờ cũng đau đầu với những câu hỏi như: nên tạo stored procedure hay không? Nên validate ở đâu (trong DBMS, trong server, hay trong client)? Object như thế có thể mở rộng không, có đáp ứng nhanh không? Vân vân và v.v...

    BOL là một lớp abstraction cho phép giải quyết những vấn đề thường gặp khi thiết kế business logic. Với một framework tốt, BOL đóng vai trò rất quan trọng vì nó là "sợi chỉ đỏ xuyên suốt các layer".

    Vì nhiệm vụ của Business Object rất đa dạng và cũng có nhiều khó khăn khác nhau nên BOL thường được đóng gói với tên gọi Application Frameworks. Lập trình viên bình thường và những dự án vừa và nhỏ ít có cơ hội tiếp xúc. Những framework thương mại chủ yếu dành cho các dự án lớn và đòi hỏi phải học chuyên sâu. Tuy nhiên, nếu đơn giản hóa vấn đề thì bạn cũng có thể tự viết một BOL cho riêng mình để tăng năng suất lập trình.

    Tham khảo: C# Expert Business Object (cuốn này hơi khó kiếm, nhưng nên có)

    Presentation Layer

    Hồi lúc trước, mình là tín đồ của nàng Athena xinh đẹp (nói cách khác là dân ghiền Delphi). Khi chuyển sang C#, mình đã thất vọng tràn trề. Lẽ ra trong Delphi thiết kế một form có master/detail view chỉ mất 1 phút thì trong C#, phải mất 2 trang code (hồi mới học thì mất cả tuần vì không hiểu làm sao để sử dụng cái datagrid). Sau đó, chuyển sang ASP.NET thì càng đau khổ hơn nữa.

    Tại sao ta lại phải khổ thế nhỉ? Viết form cực kỳ "chua" (hỏi mấy người lập trình Java với AWT thì biết). Với các ngôn ngữ hiện đại, ta có designer làm sẵn cho, chỉ việc kéo thả là xong. Các bộ "control" (thành phần điều khiển) thương mại hiện có rất nhiều, mỗi người một vẻ. Với những bộ lớn như của ComponentOne, Janus System, họ gắn luôn mác Presentation Layer vào sản phẩm. Có lý phần nào vì đó là những component phục vụ cho việc trình bày thông tin.

    Nhưng vẫn còn nhiều vấn đề:

    1/ Lệ thuộc vào control nhất định. Hãy quên chuyện thay thế grid của Winform bằng grid của Developer Express mà không cần phải sửa lại code đi nhé.

    2/ Không có chuẩn. Mỗi bộ control là một framework mới cần phải học và không tương thích gì với nhau cả. Đừng mơ có chuyện viết code năm nay, 2 năm sau quay lại thay giao diện "cái rẹt".

    3/ Logic code và UI code quyện lẫn, vào nhau. Visual Studio 2005 cố gắng giúp (lừa) bạn tránh chuyện này bằng partial class, chia code thành 2 file: aspx, code-behind (bắt chước asp.net).

    4/ Visual rất luộm thuộm. Bạn nghĩ sao nếu bạn viết chương trình đồng hồ analog (có kim giờ, phút, giây quay vòng vòng), nhưng ngày mai bạn thích đồng hồ Digital (chỉ hiển thị số). Bạn có thể nào giữ nguyên logic code, chỉ cần thay thành phần màn hình trong 5 giây không?

    5/ Data-binding: Rất phiền. Những control sẵn có khiến cho bạn trở thành "gà công nghiệp" và lệ thuộc vào nó. Điều đáng buồn là khi bạn cần nối kết dữ liệu hơi phức tạp thì vẫn cứ phải "chân lấm tay bùn", quay trở lại viết code từng dòng một, xử lý event từng chỗ một.

    Những năm gần đây xu hướng Declarative Programming gây được nhiều sự chú ý. Lấy ví dụ như thay vì viết code tạo form như sau:

    Button b = new Button();

    b.SetBounds(100,100,50,25);

    b.Text = "Click me";

    b.Click += new EventHandler(b_OnClick);

    Thì ta có thể tạo một file XML như sau:

    <Button Location="100,100" Size="50,25">Click me</Button>

    Sao giống lập trình web quá vậy? Vâng, web chính là thuở ban đầu của declarative programming. Bạn thử tưởng tượng cũng một file XML đó, bạn có thể dùng làm Windows application, bạn có thể dùng làm webform, có thể dùng cho Flash, có thể dùng cho Macintosh thì sao? Có mà nằm mơ!

    Vâng, rất tiếc rằng ở thời điểm hiện tại chưa có Presentation Layer nào thực hiện được mơ ước "viết một lần, hiển thị trên mọi hệ thống". Tuy nhiên, ít ra thì bạn không còn phải viết code từng dòng bằng tay nữa, bạn có thể nhờ Presentation Layer để tự kiểm tra đầu vào, tự sinh các form, tự dàn trang, v.v... Bạn hãy tìm hiểu thêm các chủ đề sau: Avalon, MyXaml, XAML, XAMLon, Flex, XUL...

    Mẫu thiết kế

    Mẫu thiết kế (Design Pattern) nôm na ra là cách thức giải quyết cho những vấn đề thường gặp. Điều đáng buồn là các sách về design pattern "khô như ngói, nhạt như nước ốc". Nhưng tin vui: design pattern là công cụ sẽ giúp bạn tăng lương lên gấp đôi (hoặc hơn). Đơn giản vì design pattern chính là kinh nghiệm xương máu của những người đi trước đúc kết được. Khi học design pattern, bạn sẽ có những kinh nghiệm vượt trước năng lực của mình.

    Tài liệu để đọc về design pattern hiện có rất nhiều. Mình chỉ mạn phép góp ý với các bạn một câu khi học về lĩnh vực này: "hãy nắm lấy ý tưởng, đừng chú trọng vào code". Nếu bạn chỉ nhìn vào code ví dụ, bạn sẽ dễ bị "tẩu hỏa nhập ma", sẽ bị lệ thuộc vào code, nhìn thấy cái nào cũng na ná nhau, và tệ hại nhất là chẳng biết áp dụng cho cái gì khác ngoài ví dụ ra.

    Hy vọng bài viết này sẽ giúp các bạn có được một số gợi ý để đào sâu nghiên cứu thêm. Chúc các bạn luôn "cháy bỏng" niềm đam mê lập trình.

    Nguyễn Minh Hải
    -----------------------------------------------
    Tài liệu tham khảo:
    - www.dofactory.com; www.c2.com
    - C# Design Pattern – A tutorial
    - Software Architecture Design Patterns in Java – Partha Kuchana
    * Layer và thiết kế ở mức cao hơn:
    - Enterprise Solution Patterns Using Microsoft.NET version 2.0 – MS Press
    - .NET Patterns: Architecture, Design, and Process – Christian Thilmany – Addison Wesley ISBN: 0-32-113002-2

    ID: A0606_128