2009-09-11 16 views
10

Tôi đang tìm cách tạo một singleton ở Delphi. Tôi đã làm điều này trước khi sử dụng các phiên bản cũ của Delphi, và kết thúc bằng cách sử dụng các biến toàn cục (trong phần thực hiện) và sử dụng khởi tạo và hoàn thành để xử lý cá thể. Ngoài ra không có cách nào ngăn cản người dùng tạo một thể hiện vì bạn không thể ẩn hàm tạo tiêu chuẩn. Tôi đã tự hỏi nếu có bất kỳ các tính năng mới như các nhà xây dựng lớp và destructors, và các biến lớp (ok, không quá mới), có lẽ generics, có thể giúp đỡ trong việc tạo ra một lớp singleton chung chung. Tôi chưa tạo được thứ gì cho sự hài lòng của mình.Tạo một singleton ở Delphi bằng các tính năng mới của D2009 và D2010

Trả lời

3

Đó là khả năng để quản lý này bằng cách ghi đè các phương pháp cấp phát và deallocator TRUE trong Delphi, newInstanceFreeInstance.Constructors và Destructors trong Delphi chỉ khởi tạo và hoàn thành tương ứng, chúng không phân bổ hoặc deallocate bộ nhớ, vì vậy cố gắng ẩn các nhà xây dựng luôn luôn là một chút sai lầm.

tức là bạn có thể cho phép sử dụng miễn phí bất kỳ và tất cả các nhà thầu miễn là bạn overrode NewInstance sao cho nó chỉ trả về một tham chiếu đến một cấp bộ nhớ duy nhất cho lớp.

Nhưng cố gắng thực thi mẫu sử dụng/hành vi trong một lớp cơ sở là một sự nhầm lẫn. Không phải TẤT CẢ các mẫu là hoặc yêu cầu các lớp cụ thể để đóng gói mẫu.

Trong trường hợp này, bạn sẽ tạo ra thứ gì đó không cần thiết phức tạp, và biến chứng thu hút lỗi trong kinh nghiệm của tôi và đối tượng của bài tập sau đó cố gắng tìm lỗi trong việc thực hiện mẫu và sau đó cố gắng thực hiện các biện pháp bảo vệ sai sót, thay vì nhận được với công việc thực tế của công việc mà lớp singleton được cho là để thực hiện.

Cách xa, FAR đơn giản và hiệu quả hơn để ghi lại việc sử dụng lớp học.

Documentation như một kỹ thuật để thực hiện mô hình này đã làm việc một cách hoàn hảo trong suốt 15 năm cho ApplicationScreen đối tượng trong VCL, ví dụ, chưa kể đến vô số độc thân khác mà tôi đã tạo ra trong những năm đó.

3

Đối với một singleton, bạn có thể ghi đè phương thức NewInstance. Và sử dụng biến lớp. Bạn tạo biến tại cuộc gọi đầu tiên và trả về con trỏ đến lớp của từng cuộc gọi khác.

Bạn chỉ cần tìm một số thứ để tiêu diệt nó cuối cùng (có thể sử dụng kết thúc).

+0

hoặc một destructor lớp trong D2010. –

8

Trong Delphi 2010 đến nay, cách tốt nhất và an toàn nhất là sử dụng các công cụ xây dựng lớp lớp. Xem here - đọc đặc biệt là đoạn được gọi là Đóng gói được cải tiến.

HTH.

+1

Thật đáng tiếc khi họ không bận tâm ghi lại tính năng này một cách chính xác, hoặc đưa nó vào mục "Có gì mới" trong phần trợ giúp. – IanH

7

Tôi thích sử dụng giao diện khi tôi cần đơn và ẩn việc triển khai giao diện trong phần triển khai.

lợi ích

  • phá hủy tự động khi chương trình kết thúc.
  • Không có cách nào để vô tình tạo TMySingleton.

nhược điểm

  • Ai đó có thể quyết định để thực hiện IMySingleton ngày của riêng mình.

Lưu ý: Tôi tin rằng việc sử dụng Singletons nên được giữ ở mức tối thiểu tuyệt đối. Tất cả trong tất cả, Singletons là ít hơn so với các biến toàn cầu vinh quang. Nếu và khi bạn bắt đầu đơn vị kiểm tra mã của bạn, họ trở thành một mối phiền toái.

unit uSingleton; 

interface 

type 
    ISingleton = interface 
    ['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}'] 
    end; 

function Singleton: ISingleton; 

implementation 

uses 
    SyncObjs; 

type 
    TSingleton = class(TInterfacedObject, ISingleton); 

var 
    Lock: TCriticalSection; 

function Singleton: ISingleton; 
const 
    _singleton: ISingleton = nil; 
begin 
    if not Assigned(_singleton) then 
    begin 
    Lock.Acquire; 
    try 
     if not Assigned(_singleton) then 
     _singleton := TSingleton.Create(); 
    finally 
     Lock.Release; 
    end; 
    end; 
    Result := _singleton; 
end; 

initialization 
    Lock := TCriticalSection.Create; 
finalization 
    Lock.Free; 

end. 
+0

+1 cho phần nghiêng trên đĩa đơn. – mghie

+0

@mghie: bạn không đồng ý về việc triển khai mẫu? –

+0

Tôi đồng ý với bạn rằng hầu hết việc sử dụng mẫu Singleton không có tiến bộ so với các biến toàn cục. Nhưng nếu một người muốn có chúng (vì bất kỳ lý do gì), thì một giải pháp chung chung an toàn chỉ là cần thiết. Của bạn chắc chắn là không, vì vậy bạn thực sự có thể kết thúc với nhiều hơn một ví dụ. Ít nhất nó không bị rò rỉ bộ nhớ, một cái gì đó tôi đã thấy trong các thư viện thương mại lớn, nơi các đối tượng được sử dụng để thực hiện singleton thay vì các giao diện. Giải pháp của Moritz trông giống như nó có thể an toàn chỉ, nhưng điều đó sẽ phải được kiểm tra kỹ lưỡng. – mghie

12

Nếu bạn chỉ cần một singleton đơn giản, cách đơn giản nhất là sử dụng các hàm tạo lớp và phương thức lớp theo đề xuất của nguyên đơn. Nhưng generics là rất hữu ích nếu bạn cần singletons với xây dựng theo yêu cầu (tức là trên truy cập đầu tiên).

Mã sau được lấy từ một trong các đơn vị tiện ích của tôi; về cơ bản nó cung cấp một nhà máy singleton chung cho Delphi 2009 trở đi.

interface 

type 
    {$HINTS OFF} 
    { TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding 
    expensive initialization operations. 
    If you do not require lazy creation and you target only Delphi 2010 onwards, you should 
    use class constructors and class destructors instead to implement singletons. } 
    TSingletonInstance<T: class, constructor> = record 
    private 
    FGuard: IInterface; 
    FInstance: T; 
    function GetInstance: T; 
    function CreateInstance: TObject; 
    public 
    property Instance: T read GetInstance; 
    end; 
    {$HINTS ON} 
    TSingletonFactoryFunction = function: TObject of object; 

{ Private symbols (which are in the interface section because of known limitations of generics) } 
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction); 

implementation 

{ TSingleton } 

var 
    SingletonCriticalSection: TRTLCriticalSection; 

type 
    TSingletonGuard = class (TInterfacedObject) 
    private 
    FSingletonInstance: TObject; 
    public 
    constructor Create (AInstance: TObject); 
    destructor Destroy; override; 
    end; 

    PUntypedSingletonInstance = ^TUntypedSingletonInstance; 
    TUntypedSingletonInstance = record 
    FGuard: IInterface; 
    FInstance: TObject; 
    end; 

    // TODO: is a lock required for multiple threads accessing a single interface variable? 
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction); 
var 
    USI: PUntypedSingletonInstance; 
begin 
    USI := PUntypedSingletonInstance (InstanceRecord); 
    EnterCriticalSection (SingletonCriticalSection); 
    if USI.FInstance = nil then 
    begin 
    USI.FInstance := Factory(); 
    USI.FGuard := TSingletonGuard.Create (USI.FInstance); 
    end; 
    LeaveCriticalSection (SingletonCriticalSection); 
end; 

constructor TSingletonGuard.Create (AInstance: TObject); 
begin 
    FSingletonInstance := AInstance; 
end; 

destructor TSingletonGuard.Destroy; 
begin 
    FSingletonInstance.Free; 
    inherited; 
end; 

function TSingletonInstance<T>.GetInstance: T; 
var 
    Factory: TSingletonFactoryFunction; 
begin 
    if FInstance = nil then 
    begin 
    Factory := Self.CreateInstance; // TODO: associate QC report 
    _AllocateSingletonInstance (@Self, Factory); 
    end; 
    Result := FInstance; 
end; 

function TSingletonInstance<T>.CreateInstance: TObject; 
begin 
    Result := T.Create; 
end; 

initialization 
    InitializeCriticalSection (SingletonCriticalSection); 
finalization 
    DeleteCriticalSection (SingletonCriticalSection); 

Cách sử dụng như sau:

type 
    TMySingleton = class 
    public 
    constructor Create; 
    class function Get: TMySingleton; static; 
    end; 

var 
    MySingletonInstance: TSingletonInstance<TMySingleton>; 

class function TMySingleton.Get: TMySingleton; 
begin 
    Result := MySingletonInstance.Instance; 
end; 
+2

Mã tuyệt vời Moritz. –

+0

Cảm ơn. Trong thực tế nó sẽ là đẹp hơn nếu tôi có thể loại bỏ các cách giải quyết cho D2009 :) –

0

Tôi thích tạo lớp đơn bằng cách sử dụng trình tạo mã. Vấn đề với chung là, tất cả các mã được tạo ra trong bộ nhớ, không phải trong tệp nguồn. Nó sẽ làm tăng khó khăn của việc gỡ lỗi.

4

Có cách để ẩn hàm tạo "Tạo" được thừa kế của TObject. Mặc dù không thể thay đổi cấp độ truy cập, nhưng nó có thể bị ẩn với một phương thức không tham số công khai khác có cùng tên: “Tạo”. Điều này đơn giản hóa việc thực hiện các lớp Singleton rất nhiều. Xem sự đơn giản của các mã:

unit Singleton; 

interface 

type 
    TSingleton = class 
    private 
    class var _instance: TSingleton; 
    public 
    //Global point of access to the unique instance 
    class function Create: TSingleton; 

    destructor Destroy; override; 
    end; 

implementation 

{ TSingleton } 

class function TSingleton.Create: TSingleton; 
begin 
    if (_instance = nil) then 
    _instance:= inherited Create as Self; 

    result:= _instance; 
end; 

destructor TSingleton.Destroy; 
begin 
    _instance:= nil; 
    inherited; 
end; 

end. 

tôi đã thêm các chi tiết bài ban đầu của tôi: http://www.yanniel.info/2010/10/singleton-pattern-delphi.html

Các vấn đề liên quan