2009-02-25 22 views
6

Tôi muốn buộc các lớp con thực hiện mẫu đơn.Mô hình đơn lẻ lực trong các lớp con

Ban đầu tôi nghĩ có một thuộc tính tĩnh trừu tượng trong lớp cha, nhưng khi gần hơn, điều đó không có ý nghĩa (yêu cầu trừu tượng và ví dụ).

Tiếp theo, tôi nghĩ có một giao diện với một thuộc tính tĩnh, nhưng điều đó cũng không có ý nghĩa (các giao diện cũng yêu cầu một cá thể).

Đây có phải là điều có thể, hoặc tôi nên từ bỏ ý tưởng này và thực hiện một nhà máy trừu tượng?

+0

Tôi đã ở trên hàng rào liên quan đến người độc thân trong một thời gian. Nhưng chủ đề này làm cho một số điểm tốt để không sử dụng chúng: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – JMD

+0

Có hàng trăm chủ đề, bài đăng blog và bài viết đầy đủ các lý do để sử dụng singletons. Nó là cực kỳ hiếm hoi mà họ thực sự cải thiện mã của bạn. Vui lòng suy nghĩ lại điều này. Bạn, và tất cả các nhà phát triển khác làm việc với mã đó, sẽ hối hận về việc trát vữa ở khắp mọi nơi. – jalf

Trả lời

6

Vui lòng xem xét lại. Bạn KHÔNG muốn sử dụng singletons ở đây. Bạn đang thực hiện một số chức năng có sẵn cho người dùng có được từ lớp học của bạn. Đó là tốt. Nhưng bạn cũng chỉ ra một cách cụ thể trong đó nó phải luôn luôn được sử dụng, và hoàn toàn không có lý do. Điều đó không tốt. Có thể có ý nghĩa khi chỉ khởi tạo một đối tượng của lớp này trong phần lớn thời gian, nhưng trong trường hợp đó, chỉ đơn giản là khởi tạo đối tượng sau khi. Nó không giống như bạn đang rất có khả năng vô tình nhanh chóng một chục đối tượng mà không nhận thấy. Hơn nữa, làm cách nào để bạn biết rằng có hai trường hợp sẽ KHÔNG BAO GIỜ có hữu ích không? Tôi có thể nghĩ đến nhiều trường hợp ngay cả bây giờ.

Thử nghiệm đơn vị: Bạn có thể muốn mỗi thử nghiệm để khởi tạo đối tượng này và xé nó lại sau đó. Và vì hầu hết mọi người có nhiều hơn một bài kiểm tra đơn vị, bạn sẽ cần phải thực hiện nó nhiều lần.

Hoặc bạn có thể tại một số điểm quyết định có nhiều cấp độ giống nhau/giống nhau trong trò chơi của bạn, điều đó có nghĩa là tạo nhiều phiên bản.

Một singleton cung cấp cho bạn hai điều:

  • Một sự đảm bảo rằng không có nhiều hơn một thể hiện của đối tượng sẽ không bao giờ được khởi tạo, và
  • truy cập toàn cầu để dụ

rằng Nếu bạn không cần cả hai thứ này, có những lựa chọn thay thế tốt hơn. Bạn chắc chắn không cần truy cập toàn cầu. (globals là xấu, và thường là một triệu chứng của thiết kế xấu, đặc biệt là trong dữ liệu có thể thay đổi như trạng thái trò chơi của bạn)

Nhưng bạn không cần đảm bảo rằng sẽ không có nhiều hơn một trường hợp. Đó có phải là kết thúc của thế giới nếu tôi khởi tạo đối tượng hai lần không? Ứng dụng có bị lỗi không? Nếu vậy, bạn cần đảm bảo đó. Nhưng trong trường hợp của bạn, không có gì xấu sẽ xảy ra. Người instantiating đối tượng chỉ đơn giản là sử dụng bộ nhớ nhiều hơn cần thiết. Nhưng anh ta có thể có lý do.

Đơn giản chỉ cần đặt trong tài liệu lớp học rằng đây là một lớp học rất lớn và tốn kém, và bạn không nên nhanh chóng nó thường xuyên hơn cần thiết. Đã giải quyết được sự cố. Bạn không loại bỏ tính linh hoạt có thể trở nên hữu ích sau này, bạn không cấp quyền truy cập toàn cầu vào dữ liệu mà không có lý do gì. Bởi vì bạn có thể kiểm soát ai có thể nhìn thấy đối tượng, bạn không cần phải nhấn chìm nó trong ổ khóa sẽ trở thành một nút cổ chai trong các ứng dụng đa luồng. Bạn không có phụ thuộc ẩn nằm rải rác trong toàn bộ mã của bạn, khiến bạn khó kiểm tra và khó sử dụng lại hơn.

4

Hãy thử sử dụng vùng chứa IOC. Hầu hết các container IOC tốt cho phép sử dụng mẫu đơn đơn mà không phải tự mình thực hiện nó (ví dụ: khung công tác Spring) - Tôi thích điều này tốt hơn là bắt buộc một phương thức GetInstance() tĩnh.

Bên cạnh đó, nó không thực sự có thể trong java, nó sẽ làm việc trong C++ với các mẫu mặc dù.

+0

Điều đó sẽ cho vay rất tốt đối với thiết kế tổng thể. Ngoài ra, đây không phải là cho Java nhưng đối với C#. –

+0

Mùa xuân có một thùng chứa .NET IOC. –

+0

Bạn cũng có thể thử lăn container IoC của riêng bạn nếu bạn không cần bất cứ điều gì phức tạp. Có một podcast dnrTv tốt trên làm thế nào để xây dựng một ở đây: http://www.dnrtv.com/default.aspx?showNum=126 – Joseph

0

tôi sẽ xác định một kín lớp mà được chức năng của nó từ các đại biểu thông qua với các nhà xây dựng, một cái gì đó như thế này:

public sealed class Shape { 
    private readonly Func<int> areaFunction; 

    public Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; } 

    public int Area { get { return areaFunction(); } } 
} 

Ví dụ này không làm cho rất nhiều ý nghĩa, nó chỉ minh họa một mẫu. Mô hình như vậy không thể được sử dụng ở mọi nơi, nhưng đôi khi nó giúp ích.

Thêm vào đó, nó có thể được mở rộng để lộ một số hữu hạn các lĩnh vực tĩnh:

public sealed class Shape { 
    private readonly Func<int> areaFunction; 

    private Shape(Func<int> areaFunction) { this.areaFunction = areaFunction; } 

    public int Area { get { return areaFunction(); } } 

    public static readonly Shape Rectangle = new Shape(() => 2 * 3); 
    public static readonly Shape Circle = new Shape(() => Math.Pi * 3 * 3); 
} 
+0

Toàn bộ điểm có cuộc gọi tĩnh để trả về cá thể là để các hàm tạo không có sẵn và tạo trường hợp bên ngoài lớp là không thể. Bạn giải pháp không thực hiện mô hình ông singleton. –

+0

Một lớp tĩnh luôn luôn là một singleton (có vài kịch bản, nơi mà lớp tĩnh như singleton không thể được sử dụng). Trong giải pháp này có các trường/thuộc tính tĩnh tạo ra cùng một kết quả. –

1

Tại sao? Nếu ai đó muốn sử dụng nhiều phiên bản của một lớp con của lớp của bạn, họ có thể có lý do chính đáng hợp lệ.

Nếu bạn muốn làm điều gì đó chỉ nên được thực hiện một lần cho mỗi lớp phân lớp của bạn (tại sao, tôi không biết, nhưng bạn có thể có lý do), hãy sử dụng từ điển trong lớp cơ sở.

+0

Các lớp được đề cập là các đối tượng khá lớn đại diện cho một cảnh trong trò chơi 2D.Tôi không muốn tạo lại những cảnh này nếu chúng đã được tạo trước đó và tôi muốn thông tin trạng thái của mỗi cảnh vẫn còn trong khi không sử dụng. –

+0

Bạn nên chỉnh sửa câu hỏi của mình và thêm câu hỏi này vào văn bản. Tôi nghĩ nó sẽ ảnh hưởng đến câu trả lời mà mọi người đưa ra. –

+0

Trong trường hợp đó, bạn muốn cả lớp được niêm phong và singleton. Bạn cũng có thể xem xét để đặt cảnh trong một lớp riêng biệt, đó là singleton và niêm phong, và sau đó thả yêu cầu singleton vào lớp gốc. – erikkallen

0

Tôi nghĩ bạn sẽ tốt hơn với mô hình nhà máy ở đây là trung thực. Hoặc sử dụng một công cụ IoC như Brian Dilley khuyến cáo. Trong thế giới C# có tải, đây là phổ biến nhất: Castle/windsor, StructureMap, Unity, Ninject.

Điều đó sang một bên, tôi nghĩ sẽ rất thú vị nếu bạn thực sự giải quyết vấn đề của mình! Hãy xem điều này:

//abstract, no one can create me 
public abstract class Room 
{ 
    protected static List<Room> createdRooms = new List<Room>(); 
    private static List<Type> createdTypes = new List<Type>(); 

    //bass class ctor will throw an exception if the type is already created 
    protected Room(Type RoomCreated) 
    { 
     //confirm this type has not been created already 
     if (createdTypes.Exists(x => x == RoomCreated)) 
      throw new Exception("Can't create another type of " + RoomCreated.Name); 
     createdTypes.Add(RoomCreated); 
    } 

    //returns a room if a room of its type is already created 
    protected static T GetAlreadyCreatedRoom<T>() where T : Room 
    { 
     return createdRooms.Find(x => x.GetType() == typeof (T)) as T; 
    } 
} 

public class WickedRoom : Room 
{ 
    //private ctor, no-one can create me, but me! 
    private WickedRoom() 
     : base(typeof(WickedRoom)) //forced to call down to the abstract ctor 
    { 

    } 

    public static WickedRoom GetWickedRoom() 
    { 
     WickedRoom result = GetAlreadyCreatedRoom<WickedRoom>(); 

     if (result == null) 
     { 
      //create a room, and store 
      result = new WickedRoom(); 
      createdRooms.Add(result); 
     } 

     return result; 
    } 
} 

public class NaughtyRoom :Room 
{ 
    //allows direct creation but forced to call down anyway 
    public NaughtyRoom() : base(typeof(NaughtyRoom)) 
    { 

    } 
} 

internal class Program 
{ 
    private static void Main(string[] args) 
    { 
     //Can't do this as wont compile 
     //WickedRoom room = new WickedRoom(); 

     //have to use the factory method: 
     WickedRoom room1 = WickedRoom.GetWickedRoom(); 
     WickedRoom room2 = WickedRoom.GetWickedRoom(); 

     //actually the same room 
     Debug.Assert(room1 == room2); 

     NaughtyRoom room3 = new NaughtyRoom(); //Allowed, just this once! 
     NaughtyRoom room4 = new NaughtyRoom(); //exception, can't create another 
    } 
} 

WickedRoom là lớp thực hiện đúng hệ thống. Bất kỳ mã máy khách nào cũng sẽ được giữ lại trong lớp WickedRoom đơn lẻ. NaughtyRoom không thực hiện đúng hệ thống, nhưng ngay cả lớp này cũng không thể được khởi tạo hai lần. Một instantiation 2 kết quả trong một ngoại lệ.

+0

Điều đó rất giống với triển khai hiện tại của tôi. Vấn đề của tôi là không có gì ngăn cản NaughtyRoom hoặc WickedRoom không triển khai phương thức Get của họ. Tôi muốn buộc các lớp con triển khai các phương thức Get. –

0

Mặc dù điều này sẽ không bắt buộc người dùng phải có một lớp con đơn, bạn có thể thực thi người dùng chỉ tạo một cá thể của lớp (hoặc các lớp con của nó) như dưới đây. Điều này sẽ ném lỗi nếu một thể hiện thứ hai của bất kỳ lớp con nào được tạo ra.

public abstract class SuperClass { 
private static SuperClass superClassInst = null; 

public SuperClass() { 
    if(superClassInst == null) { 
     superClassInst = this; 
    } 
    else { 
     throw new Error("You can only create one instance of this SuperClass or its sub-classes"); 
    } 
} 

public final static SuperClass getInstance() { 
    return superClassInst; 
} 

public abstract int Method1(); 
public abstract void Method2(); 
} 
Các vấn đề liên quan