Các lớp tĩnh vẫn tốt miễn là chúng được sử dụng đúng nơi.
Cụ thể: Các phương thức là phương thức 'lá' (chúng không sửa đổi trạng thái, chúng chỉ biến đổi đầu vào bằng cách nào đó). Ví dụ tốt về điều này là những thứ như Path.Combine. Những thứ này hữu ích và tạo nên cú pháp ngắn gọn hơn.
Các vấn đề tôi gặp phải với tĩnh rất nhiều:
Thứ nhất, nếu bạn có các lớp tĩnh, các phụ thuộc sẽ bị ẩn. Hãy xem xét những điều sau:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
Nhìn vào TextureManager, bạn không thể biết các bước khởi tạo phải được thực hiện bằng cách nhìn vào một hàm tạo. Bạn phải đi sâu vào lớp để tìm các phụ thuộc của nó và khởi tạo mọi thứ theo đúng thứ tự. Trong trường hợp này, nó cần ResourceLoader được khởi tạo trước khi chạy. Bây giờ hãy mở rộng quy mô cơn ác mộng phụ thuộc này và bạn có thể đoán được điều gì sẽ xảy ra. Hãy tưởng tượng cố gắng duy trì mã ở nơi không có thứ tự khởi tạo rõ ràng. Ngược lại điều này với việc chèn phụ thuộc với các phiên bản - trong trường hợp đó, mã thậm chí sẽ không biên dịch nếu các phụ thuộc không được đáp ứng!
Hơn nữa, nếu bạn sử dụng statics để sửa đổi trạng thái, nó giống như một ngôi nhà của các thẻ. Bạn không bao giờ biết ai có quyền truy cập vào cái gì, và thiết kế có xu hướng giống một con quái vật mì Ý.
Cuối cùng, và cũng quan trọng không kém, việc sử dụng static liên kết một chương trình với một triển khai cụ thể. Mã tĩnh là phản đề của thiết kế để kiểm tra. Kiểm tra mã bị lỗi tĩnh là một cơn ác mộng. Một cuộc gọi tĩnh không bao giờ có thể được hoán đổi cho một cuộc thử nghiệm kép (trừ khi bạn sử dụng các khuôn khổ thử nghiệm được thiết kế đặc biệt để mô phỏng các kiểu tĩnh), vì vậy một hệ thống tĩnh khiến mọi thứ sử dụng nó trở thành một thử nghiệm tích hợp tức thì.
Nói tóm lại, tĩnh là tốt cho một số thứ và đối với các công cụ nhỏ hoặc mã vứt đi, tôi sẽ không khuyến khích việc sử dụng chúng. Tuy nhiên, bên cạnh đó, chúng là một cơn ác mộng đẫm máu về khả năng bảo trì, thiết kế tốt và dễ kiểm tra.
Đây là một bài viết hay về các vấn đề: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/