Hello!
Hello!
So, I understand the concept of having interfaces only in your programs and I even quite agree with it. I agree with the testability of things.
However, one thing is to understand and agree to the concept and another thing is applying the technique.
That's where I am stuck.
I don't know if I am just too used to classes, that may well be, but I need a bit of help.
So these are my details.
I have an interface:
IContact = interface
['{79D205DC-FC0E-45F0-A388-56CBBFBF204A}']
procedure SetFirstName(const Value: string);
function GetFirstName: string;
procedure SetLastName(const Value: string);
function GetLastName: string;
procedure SetBirthDate(const Value: TDateTime);
function GetBirthDate: TDateTime;
property BirthDate: TDateTime read GetBirthDate write SetBirthDate;
property FirstName: string read GetFirstName write SetFirstName;
property LastName: string read GetLastName write SetLastName;
end;
Now I have a new class (I will extract its interface later, I need to implement its methods first though):
TContactList = class
private
FList: IList;
procedure SetList(const Value: IList);
function GetList: IList;
public
constructor Create;
procedure Add( AContact : IContact );
procedure Delete( AContact: IContact );
procedure SortByLastName;
property List : IList< IContact > read GetList write SetList;
end;
Now, my problem is with SortByLastName.
The constructor for the class is this:
constructor TContactList.Create;
begin
inherited Create;
FList := TCollections.CreateList;
end;
I am not sure how I should be implementing "SortByLastName" (which is a very basic functionality for a contact list application).
Any suggestions?
Please note I am making no assumptions as to what the right way to do this is, thus I am hoping someone like Stefan Glienke will chime in and help a bit :)
Kind Regards,
A
So, I understand the concept of having interfaces only in your programs and I even quite agree with it. I agree with the testability of things.
However, one thing is to understand and agree to the concept and another thing is applying the technique.
That's where I am stuck.
I don't know if I am just too used to classes, that may well be, but I need a bit of help.
So these are my details.
I have an interface:
IContact = interface
['{79D205DC-FC0E-45F0-A388-56CBBFBF204A}']
procedure SetFirstName(const Value: string);
function GetFirstName: string;
procedure SetLastName(const Value: string);
function GetLastName: string;
procedure SetBirthDate(const Value: TDateTime);
function GetBirthDate: TDateTime;
property BirthDate: TDateTime read GetBirthDate write SetBirthDate;
property FirstName: string read GetFirstName write SetFirstName;
property LastName: string read GetLastName write SetLastName;
end;
Now I have a new class (I will extract its interface later, I need to implement its methods first though):
TContactList = class
private
FList: IList
procedure SetList(const Value: IList
function GetList: IList
public
constructor Create;
procedure Add( AContact : IContact );
procedure Delete( AContact: IContact );
procedure SortByLastName;
property List : IList< IContact > read GetList write SetList;
end;
Now, my problem is with SortByLastName.
The constructor for the class is this:
constructor TContactList.Create;
begin
inherited Create;
FList := TCollections.CreateList
end;
I am not sure how I should be implementing "SortByLastName" (which is a very basic functionality for a contact list application).
Any suggestions?
Please note I am making no assumptions as to what the right way to do this is, thus I am hoping someone like Stefan Glienke will chime in and help a bit :)
Kind Regards,
A
When talking about coding against interfaces (or better abstractions) we are talking about so called service classes (or injectables as Misko Hevery calls them) - please read his article here: http://misko.hevery.com/2008/09/30/to-new-or-not-to-new/
ReplyDeleteIt is very important to differ between these two types of classes:
- newables, PODO, data objects
- injectables, service classes
You only want to make interfaces for the injectables because these are the things you want to test and possibly mock. The newables are also those classes (hence the name Misko chose) that you can just create in your program and usually don't obtain through a container (or only through a factory which you registered which you obtain from a container).
Ok so now we're saying: not everything has to be an interface, but only what is injectable.
ReplyDeleteIs that correct?
Andrea Raimondi Correct. Some of the DI examples since they are so simple might give the impression that you should do that with newables also (like the TKnight example Nick gave)
ReplyDeleteThe coding against abstractions should not be a dogma that you follow although not understanding but a way to create testable code. In in your example of a contact there is nothing to test because it does not have any function. Thus you never need to mock it and thus you don't need to code against an abstraction here.
Another thing about your original code (which is not related to your question but which I like to point out). You should go with an IList directly and implement the sorting kind over the comparer that you pass into the Sort method.
ReplyDeleteOk then but bear with me for a minute, will you :)
ReplyDeleteI am a bit stubborn and I like to explore my way around :)
Let's try and do it with newables as well :)
How would you implement SortByLastName?
Btw, my newable is created via a function that takes a TDataset parameter (that was the closest abstraction I could find).
See my previous comment. Why? Think about some added requirement. Someone says: I want to sort them by BirthDate. Then a week later we add some more properties to TContact and someone says. I want to get them ordered by City,Street and Firstname. And every time you add another SortByXXX method to your TContactList.
ReplyDeleteYou have to do that even if you have it set free.
ReplyDeleteEither that, or I am missing some crucial bit here.
A
You only have to write a comparison for the particular sorting you want to perform and pass that to the Sort method but not implement any method on the list for every sorting you want to perform. Plus you don't have an extra list type with all these SortByXX methods on which you are creating a dependency on.
ReplyDeleteI was thinking about it and I was coming to the conclusion that, really, that list has to be hidden - since it's my concrete implementation :)
ReplyDeleteMy final scope is so much bigger than this example, but I really need to lay a good foundation to this.
A
Unless I know a plausible reason (some legacy code maybe) I am going to argue that you don't need to wrap an IList and make your own implementation around it. If at all make your own implementation of IList based on TListBase or TList.
ReplyDeleteThere's a basic misunderstanding here: I am not trying to make my own implementation of the list :)
ReplyDeleteWhat I am trying to do is to encapsulate the list into my class. It's the same concept as having a specialised TObjectList that lives inside your class.
A
Well then - don't see the problem:
ReplyDeleteprocedure SortByLastname;
begin
FList.Sort(
function(const left, right: TCustomer): Integer
begin
Result := CompareText(left.Lastname, right.Lastname);
end);
end;
It works, but I am not sure how - I thought it was expecting an interface...
ReplyDeleteYou either pass IComparer or directly a TComparison (look them up in Collections.Defaults.pas)
ReplyDelete