2012-02-01 10 views
7

Z ciekawością czytam bloga Nicka Hodgesa na Why You Should Be Using Interfaces , a ponieważ jestem już zakochany w interfejsach na wyższym poziomie w moim kodowaniu, postanowiłem przyjrzeć się, jak mogę rozszerzyć to na całkiem niskie poziomy i zbadanie, jakie wsparcie dla tego istnieje w klasach VCL.Kodowanie do interfejsu TStrings i TStringList

Częstym konstrukt, że muszę to zrobić coś prostego z TStringList, na przykład ten kod załadować małą listę plików tekst w ciągu tekstowym przecinek:

var 
    MyList : TStrings; 
    sCommaText : string; 
begin 
    MyList := TStringList.Create; 
    try 
    MyList.LoadFromFile('c:\temp\somefile.txt'); 
    sCommaText := MyList.CommaText; 

    // ... do something with sCommaText..... 

    finally 
    MyList.Free; 
    end; 
end; 

Wydaje się piękny uproszczenia, jeżeli mógłbym napisać z użyciem MyList jako interfejs - byłoby pozbyć się try-finally i poprawy czytelności:

var 
    MyList : IStrings; 
     //^^^^^^^ 
    sCommaText : string; 
begin 
    MyList := TStringList.Create; 
    MyList.LoadFromFile('c:\temp\somefile.txt'); 
    sCommaText := MyList.CommaText; 

    // ... do something with sCommaText..... 

end; 

nie mogę zobaczyć IStrings zdefiniowane choć - na pewno nie w Classes.pas, chociaż istnieją odniesienia do niego w związku z programowaniem OLE online. Czy to istnieje? Czy to jest ważne uproszczenie? Używam Delphi XE2.

+4

Jeśli chcesz mojej opinii: nie rób tego! Podobnie jak nigdy nie używanie interfejsów nie jest rozwiązaniem, używanie interfejsów do wszystkiego nie jest jednym z nich. Nawet Nick twierdzi, że sprytnie TStrings/TStringList jest perfekcyjnie wykorzystywany jako instancja klasy w swoim najnowszym wpisie na blogu. –

+4

TStrings jest "prawie" interfejsem, jest klasą abstrakcyjną, która może mieć różne implementacje. Kiedy tylko jest to możliwe, przechodzę tylko wokół TStrings jako typ dla parametrów, zamiast TStringList. – mjn

+0

Zgadzam się z @UweRaabe. Interfejsy są potężne, a potężne narzędzia są często nadużywane przez początkujących. Nie używaj odwołania do interfejsu zamiast odwołania do obiektu tylko dlatego, że jest to możliwe. Zalecam stosowanie się do pierwotnego celu interfejsów - otwartego, rozszerzalnego projektu. – kludg

Odpowiedz

6

Brak interfejsu w RTL/VCL, który robi to, co chcesz (udostępnić ten sam interfejs co TStrings). Jeśli chciałbyś użyć czegoś takiego, musiałbyś to sam wymyślić.

byłoby wdrożyć go z obwolutą tak:

type 
    IStrings = interface 
    function Add(const S: string): Integer; 
    end; 

    TIStrings = class(TInterfacedObject, IStrings) 
    private 
    FStrings: TStrings; 
    public 
    constructor Create(Strings: TStrings); 
    destructor Destroy; override; 
    function Add(const S: string): Integer; 
    end; 

constructor TIStrings.Create(Strings: TStrings); 
begin 
    inherited Create; 
    FStrings := Strings; 
end; 

destructor TIStrings.Destroy; 
begin 
    FStrings.Free; // don't use FreeAndNil because Nick might see this code ;-) 
    inherited; 
end; 

function TIStrings.Add(const S: string): Integer; 
begin 
    Result := FStrings.Add(S); 
end; 

Naturalnie chcesz zakończyć resztę interfejsu TStrings w prawdziwej klasie. Zrób to za pomocą takiej klasy wrappera, aby można było owijać dowolny typ TStrings tylko poprzez dostęp do jego instancji.

Używaj go tak:

var 
    MyList : IStrings; 
.... 
MyList := TIStrings.Create(TStringList.Create); 

może wolisz, aby dodać funkcję pomocnika faktycznie zrobić brudną robotę dzwoni TIStrings.Create.

Należy również pamiętać, że całe życie może być problemem. Możesz potrzebować wariantu tego opakowania, które nie przejmie zarządzania czasem istnienia podstawowej instancji TStrings. Można to ustawić za pomocą parametru konstruktora TIStrings.


Ja uważam, że jest to interesujący eksperyment myślowy, ale nie jest to rozsądne podejście. Klasa TStrings jest klasą abstrakcyjną, która ma prawie wszystkie zalety oferowane przez interfejsy. Nie widzę prawdziwych minusów użycia tego, jak jest.

+4

Dlaczego nie wystarczy utworzyć właściwość domyślną TIStrings.Strings: TStrings, aby uzyskać dostęp bez zawijać wszystkie metody? –

+1

@Aarnaud, który sprawiłby, że kod byłby bardziej niezgrabny, ponieważ musiałbyś napisać 'Strings' dużo lub zapisać w lokalnym. Domyślna właściwość omija tylko właściwości tablicy. Ale gdybyś chciał tylko emulatora RAII, który byłby dobrym sposobem, zgaduję, że –

+1

+1 za "nie jest rozsądne podejście"!8-) – mj2008

4

Ponieważ klasa TStrings jest klasą abstrakcyjną, jej wersja interfejsu nie zapewnia wiele. Dowolny implementator tego interfejsu z pewnością byłby potomkiem TStrings, ponieważ nikt nie chciałby ponownie wdrożyć wszystkich rzeczy, które robi. Widzę dwa powody, dla interfejsu TStrings:

  1. porządki Automatyczne zasobów. Nie potrzebujesz do tego specyficznego interfejsu TStrings. Zamiast tego użyj interfejsu ISafeGuard z JCL.Oto przykład:

    var 
        G: ISafeGuard; 
        MyList: TStrings; 
        sCommaText: string; 
    begin 
        MyList := TStrings(Guard(TStringList.Create, G)); 
    
        MyList.LoadFromFile('c:\temp\somefile.txt'); 
        sCommaText := MyList.CommaText; 
    
        // ... do something with sCommaText..... 
    end; 
    

    Aby chronić wiele obiektów, które powinny mieć taką samą żywotność, używają IMultiSafeGuard.

  2. Współdziałanie z modułami zewnętrznymi. Do tego służy IStrings. Delphi implementuje ją z klasą TStringsAdapter, która jest zwracana po wywołaniu GetOleStrings na istniejącym potomku . Użyj tego, gdy masz listę ciągów i musisz przyznać dostęp do innego modułu, który oczekuje interfejsów IStrings lub IEnumString. Te interfejsy są niezdatne do użycia w inny sposób - nie dostarczają wszystkich rzeczy, więc nie używaj ich, chyba że musisz.

    Jeśli moduł zewnętrzny pracujesz ze jest coś, co można gwarancja zawsze będzie skompilowany z tej samej wersji Delphi, że moduł jest kompilowany z, należy użyć pakiety run-time i przekazać TStrings potomków bezpośrednio . Wspólny pakiet pozwala obu modułom korzystać z tej samej definicji klasy, a zarządzanie pamięcią jest znacznie uproszczone.

+0

'ISafeGuard' krzyczy o generycznych i bezpieczeństwo typu. Pisanie jest również dość trywialne. Jeśli nie korzystasz już z JCL, prawdopodobnie nie warto podejmować zależności od JCL tylko po to. –

+0

@Rob: +1 dla intrygującego rozwiązania. Fascynujące, ale prawdopodobnie nie wytłumaczalne dla moich współpracowników! –