Một ví dụ đơn giản về Strategy Pattern

Trong phát triển phần mềm nếu bạn biết cách áp dụng các mẫu thiết kế (design pattern) bạn sẽ nhanh chóng có được ứng dụng với thiết kế đơn giản nhưng hiệu quả khi bảo trì, nâng cấp hoặc mở rộng chúng. Một trong những mẫu thiết kế đơn giản và rất dễ triển khai mà tôi trình bài trong bài viết này đó là Strategy (còn có tên gọi khác là Policy). Sau khi mô tả cơ bản về nó tôi sẽ sử dụng ngôn ngữ Java để minh họa cho mẫu thiết kế này, bạn sẽ không mất quá nhiều thời gian để nắm được Strategy và áp dụng nó vào thực tế đâu, mà biết đâu bạn đã sử dụng nó mà không biết đó là một pattern nổi tiếng?!
Strategy là một trong số rất nhiều các mẫu thiết kế dành cho phát triển ứng dụng với OOP, bạn có thể tham khảo thêm các mẫu thiết kế khác ở đây: http://www.oodesign.com. Đây là pattern cho phép các giải thuật khác nhau có thể được lựa chọn trong thời-gian-chạy (run-time). Hay nói cách khác, Strategy định nghĩa một họ các giải thuật khác nhau, mỗi giải thuật được triển khai bởi một lớp (class) cụ thể và chúng có thể hoán đổi cho nhau tùy vào ngữ cảnh. Strategy giúp các giải thuật khác nhau độc lập với client sử dụng nó. Ví dụ, một lớp thực hiện nhiệm vụ so sánh dữ liệu đầu vào có thể sử dụng mẫu thiết kế Strategy để tự động lựa chọn giải thuật cho việc này dựa trên loại dữ liệu, nguồn gốc của chúng, lựa chọn của người dùng hay các yếu tố khác. Những yếu tố này không được biết cho tới thời-gian-chạy (runtime) và khi đó tùy vào loại dữ liệu mà hệ thống lựa chọn cách thức so sánh khác nhau. Các giải pháp so sánh được đóng gói trong các đối tượng riêng biệt sẽ được sử dụng bởi những đối tượng thực hiện việc này tại các phân vùng khác nhau của hệ thống (hoặc thậm chí ở những hệ thống khác nhau) mà không gây ra sự trùng lặp về mã lệnh.Strategy thường được biểu diễn bằng UML như sau:

Sau đây tôi sẽ sử dụng Java để minh họa một ví dụ cụ thể về Strategy.

1. Sử dụng Interface có sẵn trong Java đó là java.lang.Comparable,  interface này có phương thức compareTo() cho phép các lớp triển khai nó thực hiện việc so sánh hai đối tượng với nhau.

Bạn có thể tìm hiểu thêm Interface này ở đây:http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html

2. Triển khai 2 lớp kế thừa Comparable, lớp Student thể hiện các đối tượng sinh viên, Product thể hiện các đối tượng sản phẩm.

Hai lớp này sẽ cài đặt phương thức compareTo() để so sánh các đối tượng Sinh viên với nhau hoặc các đối tượng Sản phẩm với nhau. Khi đó ta có sơ đồ quan hệ giữa các lớp và interface như sau:

Sau đây chúng ta cùng quan sát mã nguồn của 2 lớp này:

Lớp Student:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Student implements Comparable<Student> {
   private String rollNo;
   private String fullName;
   private double marks;
   public Student(String rollNo, String fullName, double marks) {
      this.rollNo = rollNo;
      this.fullName = fullName;
      this.marks = marks;
   }
   /* Hai sinh viên so sánh với nhau theo tên (fullName) */
   @Override
   public int compareTo(Student o) {
      if (o==null || o.fullName==null) {
         return 1;
      }
      if (this.fullName==null) {
         return -1;
      }
      return this.fullName.compareTo(o.fullName);
   }
   @Override
   public String toString() {
      return rollNo + " - " + fullName;
   }
}

Các giá trị trả về tùy thuộc vào đối tượng sinh viên kia hoặc tuy thuộc vào việc so sánh hai thuộc tính fullName của 2 đối tượng sinh viên.

Lớp Product:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Product implements Comparable<Product> {
   private String serial;
   private String productName;
   private double price;
   public Product(String serial, String productName, double price) {
      this.serial = serial;
      this.productName = productName;
      this.price = price;
   }
   /* Hai sản phẩm so sánh với nhau theo giá bán */
   @Override
   public int compareTo(Product o) {
      if (o==null || this.price>o.price) {
         return 1;
      }
      if (this.price<o.price) {
         return -1;
      }
      return 0;
   }
   @Override
   public String toString() {
      return serial + " - " + productName;
   }
}

Các giá trị trả về tùy thuộc vào đối tượng sản phẩm kia hoặc tùy thuộc vào việc so sánh hai thuộc tính price của 2 sản phẩm với nhau.

3. Chương trình minh họa

Chương trình dưới đây thể hiện việc so sánh giữa các đội tượng sinh viên với nhau và giữa các sản phẩm với nhau. Trong phương thức compare(), Collections (đóng vai trò của lớp Context trong hình vẽ UML mô tả Strategy ở trên) cài đặt phương thức sort() tự xác định loại đối tượng và sử dụng phương thức compareTo() cho phù hợp.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Client {
   public static void compare(ArrayList list) {
      if (list != null && list.size()>0) {
         Collections.sort(list);
         for (Object obj : list) {
            System.out.println(" + " + obj);
         }
      }
   }
   public static void main(String[] args) {
      ArrayList<Student> students = new ArrayList<Student>();
      students.add(new Student("A01234""Minh Le Hoang"12.5));
      students.add(new Student("A01235""An Nguyen Van"15.5));
      students.add(new Student("A01235""Tuan Nguyen Anh"13.5));
      students.add(new Student("A01235""Ha Le Hoang"17.5));
      System.out.println("Sap xep sinh vien: ");
      //Hệ thống sẽ dùng phương thức compareTo() của lớp Student để so sánh các đối tượng Sinh viên:
      Client.compare(students);
      ArrayList<Product> products = new ArrayList<Product>();
      products.add(new Product("P0023""Dell Vostro 3400"1200));
      products.add(new Product("P0012""IBM Thinpad T60"1100));
      products.add(new Product("P0003""Vaio Z"3000));
      products.add(new Product("P0303""HP Pavilon"1230));
      System.out.println("Sap xep san pham: ");
      //Hệ thống sẽ dùng phương thức compareTo() của lớp Product để so sánh các đối tượng sản phẩm với nhau:
      Client.compare(products);
   }
}

Qua VD đơn giản này chắc bạn đã phần nào hiểu được về Strategy Pattern và sự hiện diện của nó trong các thiết kế sẵn có của Java như bạn đã thấy trong Comparable, mong rằng bạn sẽ áp dụng được mẫu thiết kế này cho các bài toán cụ thể của mình :o)

Link gốc: http://www.csharpvn.com/KienThuc_ChiTietKienThuc.aspx?Id=318

 

Advertisements

Func vs. Action vs. Predicate

Khi nào bạn cần sử dụng Delegate Func, khi nào dùng Action

– Đơn giản là khi bạn gọi hàm mà cần trả về giá trị thì dung Func, nếu không thì dùng Action (kiểu như void trong gọi method bình thường)

Link chi tiết:

1. http://stackoverflow.com/questions/4317479/func-vs-action-vs-predicate (cái này chi tiết)

2. Example:

using System;
using System.Collections.Generic;
namespace FuncExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<string, string, string> concatTwo = (x, y) => string.Format("{0} {1}",x,y);
            Func<string, string, string, string> concatThree = (x, y, z) => string.Format("{0} {1} {2}", x, y,z);
            Console.WriteLine(concatTwo("Hello", "Jalpesh"));
            Console.WriteLine(concatThree("Hello","Jalpesh","Vadgama"));
            Console.ReadLine();
        }
    }
}

Kendo UI

Nó là một Frameword hỗ trợ toàn diện HTML 5, Javascript cho việc phát triển ứng dụng web và Mobile

1. Download trial 30 ngày cho ASP.NET MVC, JSP, PHP (giá thì hơi chát $999).

http://www.kendoui.com/download.aspx

2. Link demo cho Mobile

http://demos.kendoui.com/mobile/overview/index.html#/

3. Kendo UI for ASP.NET MVC

http://www.kendoui.com/server-wrappers/mvc.aspx

4. So sánh sencha và Kendo

http://www.sencha.com/forum/showthread.php?229886-Sencha-or-Kendo

Note: Nếu cần chọn lựa tốc độ cho trình duyệt thì Kendo là giải pháp tốt hơn vì từ Ext 4.x  phải đối diện với tốc độ chậm trong các ứng dụng thực tế.

Applying the Chain of Responsibility Design Pattern

Earlier this week I made use of the Chain of Responsibility design pattern to solve a fairly straight-forward problem. I’ve always been careful when trying to apply design patterns, it’s easy to get carried away and try to force a design pattern to a problem that doesn’t really require it. In this case, I felt it was justified and hopefully you’ll think the same!

The Context

I’ll give some context to the problem before we go into exactly what the Chain of Responsibility pattern is. I was working on a HTTP service (built on ASP.NET Web API) which had an API controller action that accepted requests for some data stored in a persistent repository. My requirement was to inspect the incoming request object, and depending on certain characteristics of the request, log the request object data to zero or many different output locations. This is to enable reporting on the types of requests that the service receives. The output locations were disparate data stores such as an XML file, Excel file and SQL Server Database. The key point was that to log the request I had to inspect the content of the request, and depending on some criteria, log it to none or certain data stores. Furthermore, certain requests had to be ignored and therefore not logged.

Chain of Responsibility

When I read this requirement I knew immediately there was a design pattern for this type of problem. I had taken an Object-Oriented design pattern course at university around six years ago but couldn’t quite remember the name of the pattern. After having a quick look on the GoF website, I eventually found it – Chain of Responsibility (CoR). CoR is a software design pattern that has been classified into the “behavioral” category of patterns – these are patterns that broadly deal with objects interacting with other objects. The idea is that you have multiple handler objects chained together just like a linked list data structure. An incoming request object is passed over to the first handler in the chain, the handler (depending on its “responsibility”), inspects the request and can either:

  1. Handle the request and not pass the request down the chain of handlers
  2. Handle the request but still pass the request down the chain of handlers
  3. Not handle the request and pass the request down the chain to the next handler

Note that before a handler sends the request object down the chain, it is also responsible for checking if it is actually linked to another handler. If a handler isn’t linked to another handler, it is the last handler in the chain and therefore consumes the request.

Applying CoR to the Problem

In my case, the incoming data request to the API was my CoR request that required “handling” from a logging perspective. I implemented a separate concrete logging handler class for each of the concrete data stores (XML, Excel and SQL Server). Each logging handler inspected the request and logged the request if it matched the criteria for the handler before passing the request to the next handler. Therefore, in my case, the CoR handler behaviour corresponded to point two above.

The Implementation

Note that I’ve stripped down the code below (especially for each concrete handler) so that it is easier to see what’s going on from a design pattern perspective.

The first step was to write an abstract base handler that defined the behaviour that each of my handlers will support. The HandlerBase class below is generic enough to be reused in any case where you require a CoR implemented. The single type parameter “TRequest” is the type of the object that will go through your chain (can be any .NET type).

public abstract class HandlerBase<TRequest>
{
    public HandlerBase<TRequest> Successor
    { 
        protected get; 
        set; 
    }

    public abstract void HandleRequest(TRequest request);
}

The HandlerBase class has one property, which allows users of a handler to set a successor to this handler (the next handler in the chain) and it has an abstract method that all derived classes will need to implement – HandleRequest. You will see further below how we use the Successor property when setting up our chain, and how each concrete handler (a class that derives from HandlerBase) uses the HandleRequest method on its successor to pass the request down the chain.

Next, I implemented each of my concrete handlers – that is, a handler for logging to an XML document, Excel document and SQL server.

public class LogRequestToXmlHandler : HandlerBase<ApiDataRequest>
{
    public override void HandleRequest(ApiDataRequest request)
    {
        if (request.Foo == Something && request.Bar == SomethingElse)
            // Log request to xml document

        // Pass on the request to the next handler, if any
        if (base.Successor != null)
            base.Successor.HandleRequest(request);
    }
}

Notice how the concrete handler LogRequestToXmlHandler derives from HandlerBase and passes the type of the object that is going through the chain – ApiDataRequest. Also notice that because we’re inheriting from an abstract base class containing one abstract method – we’re forced by the compiler to override the HandleRequest method. This method accepts one parameter, the request object. It is in this method that you will place any specific handling logic for the request object – I’ve added some pseudo-code to demonstrate this. The last couple of lines inspect the Successor property (inherited from the base class), if it isn’t null – then we call the HandleRequest on it, thus passing our request down the chain. If Successor returns null, then the current handler is the last in the chain and we do nothing – effectively consuming the request. For completeness, the other handlers are below.

public class LogRequestToExcelHandler : HandlerBase<ApiDataRequest>
{
    public override void HandleRequest(ApiDataRequest request)
    {
        if (request.Foo == Something && request.Bar == SomethingElse)
            // Log request to excel document

        if (base.Successor != null)
            base.Successor.HandleRequest(request);
    }
}

public class LogRequestToDbHandler : HandlerBase<ApiDataRequest>
{
    public override void HandleRequest(ApiDataRequest request)
    {
        if (request.Foo == Something && request.Bar == SomethingElse)
            // Log request to database

        if (base.Successor != null)
            base.Successor.HandleRequest(request);
    }
}

Now that we have our handlers, the final step is to setup our chain so that it’s ready to handle requests. The code below shows how the handlers are chained together.

HandlerBase<ApiDataRequest> logToXmlHandler
    = new LogRequestToXmlHandler();
HandlerBase<ApiDataRequest> logToExcelHandler
    = new LogRequestToExcelHandler();
HandlerBase<ApiDataRequest> logToDbHandler
    = new LogRequestToDbHandler();

logToXmlHandler.Successor = logToExcelHandler;
logToExcelHandler.Successor = logToDbHandler;

The first step above was to initialise each handler that will be going into the chain. We then use the Successor property to chain the three handlers together. The beauty of using this pattern is that you can chain however many handlers you want and even make the chain configurable so that it is setup at runtime depending on some settings in a config file.

Now, when a new request came through my API, it was a simple case of invoking the HandleRequest method on the first handler in the chain and passing the ApiDataRequest object through as a parameter. This sent the object down the chain, allowing each log handler to inspect it and decide whether to log it to their individual outputs.

logToXmlHandler.HandleRequest(request);

* Update *

One thing that I missed in this post and is worth mentioning is a scenario where you don’t want each concrete handler to decide whether to pass the request on down the chain. In this case, the request would go to every handler in the chain regardless. One way to accomplish this is to update the HandlerBase class as follows:

public abstract class HandlerBase<TRequest>
{
    public HandlerBase<TRequest> Successor
    { 
        protected get; 
        set; 
    }

    public void HandleRequest(TRequest request)
    {
        Handle(request);

        if (Successor != null)
            Successor.HandleRequest(request);
    }

    protected abstract void Handle(TRequest request);
}

The updated HandlerBase now implements a public HandleRequest method which first delegates the handling logic to an abstract method (Handle) which will be overridden by a concrete handler. Once a request has been handled, a test is made to check if there is a successor and if there is, then the request is passed on. What this now means is that each concrete handler will implement just one method, Handle, and not need to worry about whether to pass the request on or not – that is done automatically in the HandleRequest method. An example concrete handler is below:

public class LogRequestToXmlHandler : HandlerBase<ApiDataRequest>
{
    protected override void Handle(ApiDataRequest request)
    {
        // Check request and log it, if required
    }
}

Chain of Responsibility Pattern Example in C#

he Chain of Responsibility pattern takes a request and flows through a chain of handlers. Each handler checks if it can handle the request according to some business rules. If the handler can’t handle the request it gets passed to the next handler in the chain. The client doesn’t know which handler eventually handles the request.

In this example we send a parcel with a certain value. Some of the packages are insured. Based on the following criteria a package gets a certain treatment. For uninsured parcels with a value lower than 50 there is the budget treatment (you don’t want that). Parcels with a uninsured value from 50 and up get a normal treatment. All insured packages get a treatment deluxe. The chain of handlers is set up in that order.

Chain Of Responsibility Diagram

The base handler takes care of registering the next handler in the chain. A concreet implementation of a handler is show below. The ParcelHandlerBudget is the first in the chain of responsibility and checks if it can handle the parcel in the Handle() method. If so, we return a message with some hints to the kind of treatment the parcel is getting. If not, the NextHandler’s Handle() method is called.

namespace ChainOfResponsibilityPattern
{
    public class ParcelHandlerBudget : ParcelHandlerBase
    {
        public override string Handle(Parcel parcel)
        {
            if (parcel.Value < 50m && !parcel.IsInsured)
            {
                return "BUDGET: " +
                       "Assault package at a discount price";
            }

            return NextHandler.Handle(parcel);
        }
    }
}

In the client we create a parcel list with different (un)insured values and instantiate and chain up the parcel handlers. For every package on the list we walk up the chain of responsibility by calling Handle() on the first handler. The output of the ‘winning’ handler is returned and written to the console.

using System;
using System.Collections.Generic;

namespace ChainOfResponsibilityPattern
{
    internal class Program
    {
        private static void Main()
        {
            var parcelList =
                new List<Parcel>
                    {
                        new Parcel {IsInsured = false, Value = 2500m},
                        new Parcel {IsInsured = true, Value = 10m},
                        new Parcel {IsInsured = false, Value = 10m},
                        new Parcel {IsInsured = true, Value = 0m},
                        new Parcel {IsInsured = false, Value = 45m},
                        new Parcel {IsInsured = false, Value = 5m},
                        new Parcel {IsInsured = false, Value = 150m},
                    };

            var budgetHandler = new ParcelHandlerBudget();
            var normalHandler = new ParcelHandlerNormal();
            var deluxeHandler = new ParcelHandlerDeluxe();

            budgetHandler.SetNewHandler(normalHandler);
            normalHandler.SetNewHandler(deluxeHandler);

            foreach (Parcel parcel in parcelList)
            {
                try
                {
                    string result = budgetHandler.Handle(parcel);
                    Console.WriteLine(result);
                }
                catch (Exception)
                {
                    Console.WriteLine("Parcel rejected");
                }
            }

            Console.ReadKey();
        }
    }
}

Chain Of Responsibility Console

This design makes it easy to extend, change and maintain serial chain of responsibility rules. Further and recommended readings on the subject by Anoop Madhusudanan and Kory Becker.

Download the source from GitHub.

 

Link gôc: http://www.remondo.net/chain-of-responsibility-pattern-example-in-c/

Chain of Responsibility Pattern (thực hiện 1 chuỗi các tác vụ có trình tự)

Mẫu số 1: Chain of Responsibility Pattern

Chain of Responbility để làm gì???
Giống như tên gọi của nó … Chain of Responsibility là một mẫu thiết kế giải quyết cho việc thực hiện 1 chuỗi các tác vụ có trình tự mà mỗi 1 tác vụ trong chuỗi đó được đảm nhiệm bởi 1 class.

Ví dụ :
Mua bột –> Nhào bột –> Nướng bánh –> Nhậu

Đó là ví dụ đơn giản về 1 chuỗi các công việc. Mỗi công việc ở trên do một người đảm nhiệm:

phụ việc : đi chợ
phụ bếp : nhào bột
Đầu bếp : Nướng bánh
Thực khách : nhậu

Trong đó Phụ việc, phụ bếp, đầu bếp, thực khách là các class và Đi chợ(), nhào bột(), Nướng bánh(), nhậu() là các phương thức (method).

Hãy tưởng tượng đến 1 sợi dây xích có nhiều mắc xích …

Trong mẫu Chain of Responsibility chúng ta có một thành phần chính là Handler. Handler là đối tượng đóng gói phương thức xử lý của instances này vào 1 instances khác (xem phần sau sẽ rõ)

Tạm quên UML của nó 1 bên vì nó không cung cấp nhiều thông tin lắm về mẫu này … ta đến với vấn đề mẫu…

Vấn đề: Một nhóm các class xử lý theo lượt nhưng không có cách nào xác định thứ tựcái nào sẽ được gọi trước.

Ví dụ 1 luồng công việc như sau

 Process firstProcess = new FirstProcess();  
 fistProcess.Run();  
 Process secondProcess = new SecondProcess();  
 secondProcess.Run();  
 Process thirdProcess = new ThirdProcess();  
 thirdProcess.Run();  

Trong đó Process là lớp cha … các lớp FirstProcess, SecondProcess, ThirdProcess() là các lớp kế thừa.

Giải quyết: Sử dụng một chuỗi class liên hợp để xử lý và định nghĩa bước thực hiện tiếp theo trong hàm xử lý của các class này.

Sơ đồ UML

Chúng ta đặt class thực thi tiếp theo vào class hiện tại, cho phép mỗi class chứa thể hiện (instance) của class tiếp theo trong chuỗi mắc xích.

#RunNext() bao gồm Run() và SetNextProcess()… 2 hàm này sẽ đảm bảo tại mỗi một mắc xích nó sẽ làm đúng nhiệm vụ của mình và ủy thác phần còn lại cho “mắc xích” tiếp theo cho đến khi hết.

Code cài đặt :

 abstract class Process  
 {  
   private Process _nextProcess;  
   protected abstract void RunNext();  
   public void Run()  
   {  
      RunNext();  
      if(_nextProcess != null)  
      {  
        _nextProcess.Run();  
      }  
   }  
   public void SetNextProcess(Process process)  
   {  
      _nextProcess = process;  
   }  
 }  
 class FirstProcess : Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(1000);  
   }  
 }  
 class SecondProcess: Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(2000);  
   }  
 }  
 class ThirdProcess: Process  
 {  
   Protect override void RunNext()  
   {  
     System.Threading.Thread.Sleep(3000);  
   }  
 }  

Thực thi vấn đề

 Process firstProcess = new FirstProcess();  
 Process secondProcess = new SecondProcess();  
 Process thirdProcess = new ThirdProcess();  
 firstProcess.SetNextProcess(secondProcess);  
 secondProcess.SetNextProcess(thirdProcess);  
 thirdProcess.SetNextProcess(null);  
 firstProcess.Run();  

Kết quả:

 Beginning first process ....  
 Ending first process ....  
 Beginning second process ....  
 Ending second process ....  
 Beginning third process ....  
 Ending third process ....  

Vậy ta đã thực thi được 1 chuỗi các hành động theo mắc xích bằng cách “chỉ ra mắc xích” tiếp theo của mắc xích hiện tại… Đó cũng là tinh thần của Chain of Responbility…

 

Link gốc: http://sinhvienluoi.blogspot.com/2012/09/chain-of-responsibility-pattern.html

Filio – Distributed File Management

Link: http://www.codeproject.com/Articles/98494/Filio-Distributed-File-Management

congnt:

Đây là một ví dụ về tìm kiếm và xử lý dữ liệu file phân tán khá hay.
Tác giả sử dụng một số kỹ thuật lập trình cao

  • Lập trình song song
  • Tăng tốc tìm file NTFS – NTFS’s Master File Table and USN Journal (phần này Hòa có thể kế thừa để cải tiến lưu ảnh, truy xuất file)
  • file phần tán sử dụng HTTP, FTP, TCP…
  • xử lý dữ liệu nhiều 3TB.

Hiện tại hệ thống của mình sẽ làm việc với file nhiều khi phần dự án logfile mới sẽ đẩy dữ liệu ra file luôn, việc quản lý file backup data có nhiều bất cập.

Hoapt:

Bản thân cách sử dụng NTFS Journal này được áp dụng trong các thiết bị lưu trữ hiện đại dùng trong server như NAS hoặc SAN. NTFS 3.1 từ 2008, Win7 trở lại đây cũng áp dụng cách này để nâng cao mức độ truy xuất và không phải sử dụng trình defagmenter như các bản windows trước nhưng ko tối ưu cho lắm đối với các hệ thống cập nhật liên tục như của BA. MS cũng có ý tưởng về hệ thống lưu trữ dữ liệu ở bất cứ đâu, cụ thể SQL 2012 có hệ thống quản lý file phân tán y hệt như thế này.

Ý tưởng sau em định dùng SAN nó sẽ làm việc tốn hơn với SQL và cả NoSQL, còn nếu ai rỗi thì có thể dùng chú này xem sao.

 

So sánh các cách Serialize sử dụng Protobuf.

http://stevenhollidge.blogspot.com/2011/07/comparing-protobuf-net-serialisation.html