Showing posts with label Delphi. Show all posts
Showing posts with label Delphi. Show all posts

Sunday, 8 December 2024

Should naming procedures Setup and Teardown be reserved for unit testing?

I recently came across some code a developer had done where they had named 2 public procedures 'Setup' and 'Teardown', when I fist saw these I instinctively thought of unit tests and thought that class might relate to unit testing. After looking into the code it was apparent that these procedures where meant to be called after an object of the class was created and before the object was freed, but had nothing to do with unit testing.

Even though there are no restrictions on using these words for procedures I do think it is best practice not to use them in production code and name them something different like 'InitializeResources' and 'CleanupResources'. I think keeping procedures 'Setup' and 'Teardown' specifically for testing maintains a clear distinction between testing and application logic, which can be beneficial for maintainability.

Wednesday, 24 July 2024

Why change how a Web Broker service works?

I have a web service (web broker) that works fine and each request is sent to a corresponding object that relates to the request and the response is returned, for example the /customerinfo request (GET) calls an object that is private to the TWebModule that returns customer information.

procedure TMyWebModule.CustomerInfoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
    Response.Content := FCustomers.CustomerInfo(Request);
end; 

BTW, this is not the actual code, but is just an example. This works fine and has been working fine for some time. However, another developer has come up with what they consider a better way fo doing this. First, they add a property of TWebRequest to all objects that use TWebRequest, so in this example the FCustomer object has a WebRequest property. Then in the BeforeDispatch set the WebRequest property of all the web module objects (not just the one that relates to the request) to the web request. The advantage of this is so that you do not need to pass in the WebRequest in the action e.g.

procedure TMyWebModule.CustomerInfoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
    Response.Content := FCustomers.CustomerInfo;
end; 

They have also proposed that we could also set the TWebResponse on all the objects to that then all you need to do is this.

procedure TMyWebModule.CustomerInfoAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
    FCustomers.CustomerInfo;
end; 

The other change that is being proposed is to add validation to the TWebRequest, by having a new class that descends from TWebRequest, lets call it TMyWebRequest and this will have validation code, so that you can call something like MyWebRequest.Validate it will do some validation. Currently there is a validation class that takes the TWebRequest, but this would be removed and the TMyWebRequest would validate itself.

In the AfterDispatch method it will iterate through all the objects and set the MyWebRequest property to nil.

There are a few things I am not keen about with making these changes. 

  • I don't like the idea of descending from TWebRequest just to add some validation methods, and much prefer having validation classes that are responsible for doing the validation.
  • Currently passing TWebRequest makes it clear and explicit that the parameter is expected, making it easier to trace.
  • The current way keeps the responsibility of handling the request and response with the action.
  • It is setting the MyWebRequest property of all the objects, even though it is only required for the object that will be called, for example it sets the MyWebRequest property on the Order, Product, System and Customer objects even though it is only the Customer object that will be called with that requrest.
  • It means that a developer can use both methods, this could cause confusion.
  • It will take some time to change all the services to work this new way. 

The benefits I can see from doing this are:

  • Reduce the number of parameters passed.
I cannot see any real benefits from making these changes, or am I missing something?


Friday, 24 November 2023

How to write try finally blocks

 I recently came across code another developer had done and they write try..finally blocks like the following:

procedure TSomeClass.DoSomething: string;
var
    myClass: TMyClass;
begin
    myClass := nil;
    try
        myClass := TMyClass.Create;
        // Do stuff
    finally
        myClass.Free;
    end;
end;

Were I would write it as follows:

procedure TSomeClass.DoSomething: string;
var
    myClass: TMyClass;
begin
    myClass := TMyClass.Create;
    try
        // Do stuff
    finally
        myClass.Free;
    end;
end;

I always create the object on the line before the try and would not set the object to nil just before creating it. I believe setting the object to nil before creating it and putting the create after the try to not be the correct way and do not know the reasoning why the developer does it like this.


Friday, 14 September 2018

Does Delphi have a future?

I've been using Delphi for almost 20 years now, I have worked in various industries and have also used other languages and IDEs like C#, Xcode, VB.Net, but while developing in other languages I have always been doing something with Delphi and I am a big fan of it. There are many reasons why I enjoy and have a preference to develop some applications in Delphi, but recently I have one problem with it.

The problem I have with Delphi is its future and the question that gets raised 'how easy is it to employ new developers?'. In the UK it is almost impossible to employ a good Delphi developer and can take years to find one. The other option is to employ a developer with good OOP skills and knowledge of a similar language like C# and for them to learn the language (currently no Pluralsight courses). The problem with employing a developer who does not know Delphi is they do not want to learn Delphi, it is not a big enough carrot on the stick and salaries in the UK are less than other languages.

What I would like to know are the selling points of Delphi to a young person who knows OOP and has some experience in software development. I asked myself the question, 'if I was young and needed to choose a language and development environment, would I invest my future in Delphi?' The short and current answer is 'No'.

Wednesday, 24 January 2018

Can and should user access permissions be stored in Active Directory?

I am currently looking at various designs of how to store user permissions, it is most likely I will choose on a Role Based Access Control (RBAC) and my original thought was to store the various role permissions in the database and in the application there would be an area that would allow an administrator to control the various user roles and role permissions. But some developers have mentioned to me not to do this and allow 'Active Directory' to control the various user permissions. This would mean that the access definitions (rules) would not need to be in the database and the control of these permissions do not need to be done in an application. I imagine the access enforcement to the various areas of the software e.g. Purchase Orders would still need to be controlled by the application and how the application interrogates the active directory permissions I still do not know.

The developers who have suggested doing this give the impression that it is possible and easier (saving time) than doing the access control using the database and application, although they have never done it themselves. Before I journey into how to develop for Active Directory, does anyone have experience of using it for access control for multiple areas of a Delphi application, and is this the direction access control is going for applications?   

Wednesday, 15 March 2017

Why are semi-colons sometimes not required?

Over the 20 years I've been developing commercial software one question I still have with the Delphi language is why was the language designed so semi-colons are not required at the end of methods? For example:

function DoSomething: boolean;
begin
  if FDoSomethingElse then
  begin
    DoSomethingElse;
  end
end; 

I believe the reason why the compiler does not complain about this, is because the semi-colon in Delphi is a statement separator and not a terminator. If I modify this function for example:

function DoSomething: boolean;
begin
  if FDoSomethingElse then
  begin
    DoSomethingElse;
  end;
  DoOtherStuff
end;

I obviously need to now add in the semi-colon, but I do not need to add in the semi-colon after 'DoOtherStuff'. I currently cannot see any benefit of leaving the semi-colon out. I would not normally code this way and always add in the semi-colon for 2 reasons:

  1. Adding the semi-colon makes the code more consistent.
  2. It means later on when the code is modified the semi-colon does not have to be added.
I have come across Delphi developers who will not put in the semi-colon if it is not required, but cannot see any real benefit from leaving it out. Maybe I have answered my own question, and that is the semi-colon is a separator and when the language was developed they did not see any pros or cons of just allowing the semi-colon not to be there.


Monday, 31 October 2016

Solution to Delphi IDE Freezing Problem

Over the last few years I've been using Delphi XE, and on quite a few occasions the IDE can just freeze for what seems to be no reason. It does not appear to be an issue with small projects, but mainly larger projects and might relate to code insight and how I have it configured. When this problem occurred I use to 'End Task' in task manager, which meant potentially losing some work, but the best solution for when this happens to to find the directory that contains all the 'dcu' files and delete them.

Friday, 2 September 2016

Delphi Vs Visual Studio

This is a comparison between developing software in Delphi XE+ vs Visual Studio 2015 (C#). I’ve decided not to go to in-depth with regards to syntax or run time dependencies of executables. I have experience in both although I have mainly developed web service and WCF services in C#, and mainly developed desktop applications in Delph, so that might affect some of my opinions.

Delphi Pros

  •          Quick and easy to develop desktop applications.
  •          I prefer the form designer, because I find it easier to use.
  •          Quick to compile projects.
  •          I like the code structure of interface and implementation in the same file.
  •          You can develop mobile apps.


Delphi Cons

  •          Code insight is slow and could be significantly improved.
  •          Some areas of the IDE look dated and it needs investment to keep it up to date with its competitors.
  •          Find declaration does not always work, finding the function or procedure is more time consuming than Visual Studio.
  •          Hard to employ developers as there are not many about.
  •          Code insight is slow to update, for example add a new property to a class and sometimes you need to compile to make that property available for code insight.
  •         Unit testing using DUnit feels old and there has been little development of it over the years.

  

Visual Studio Pros


  •          IDE has a more modern interface, like the option for a dark color theme.
  •          Code IntelliSense is great, and I like the ‘potential fixes’ option which is very useful for adding directive or assembly reference.
  •         Unit testing is easy and integrated into the IDE, making it easy to run tests while you are developing.
  •          Debugging services is easier than when I have developed services in Delphi, this might have changed recently in later versions of Delphi.
  •         Peek definition is a nice feature.

Visual Studio Cons


  •         Slow to start-up.
  •          Sometimes freezes and it is best just to wait.

Tuesday, 21 June 2016

Delphi error XML document must have a top level element. line 0

I have a Delphi application written in XE that consumes a WCF service written in C#, I have been receiving the following error message with one of the methods

XML document must have a top level element. line 0

I managed to fix the issue by making the method in the WCF Service return a value (boolean) if successful. It is possible Delphi does not like WCF Service methods that are one way ([OperationContract(IsOneWay = true)]).

Sunday, 19 July 2015

How to draw an arrow head

I've worked on a few developments that require graphical input form users like CAD, flow control and diagram applications. Here is a snippet of code that draws an arrow head to a line.

This is an example and in real code you might want to pass the arrow head length and width in as parameters.




procedure TMainForm.DrawArrowHead(const x1, y1, x2, y2: integer;
  Canvas: TCanvas);
var
  HeadLength : real;
  HeadWidth : real;
  xbase : Integer;
  xLineDelta : Integer;
  xLineUnitDelta : Double;
  xNormalDelta : Integer;
  xNormalUnitDelta : Double;
  ybase : Integer;
  yLineDelta : Integer;
  yLineUnitDelta : Double;
  yNormalDelta : Integer;
  yNormalUnitDelta : Double;
  OrigBrushColor : TColor;
begin
   OrigBrushColor := Canvas.Brush.Color;
   Canvas.Brush.Color := clBlack;
   xLineDelta := x2 - x1;
   yLineDelta := y2 - y1;
   xLineUnitDelta := xLineDelta / SQRT( SQR(xLineDelta) + SQR(yLineDelta));
   yLineUnitDelta := yLineDelta / SQRT( SQR(xLineDelta) + SQR(yLineDelta));
   // (xBase,yBase) is were the arrow line is perpendicular to base triangle.
   HeadLength := 12;
   HeadWidth := 2;
   xBase := x2 - ROUND(HeadLength * xLineUnitDelta);
   yBase := y2 - ROUND(HeadLength * yLineUnitDelta);

   xNormalDelta := yLineDelta;
   yNormalDelta := -xLineDelta;
   xNormalUnitDelta := xNormalDelta / SQRT( SQR(xNormalDelta) + SQR(yNormalDelta));
   yNormalUnitDelta := yNormalDelta / SQRT( SQR(xNormalDelta) + SQR(yNormalDelta));
   //Draw the arrow tip
   Canvas.Polygon([Point(x2,y2),
     Point(xBase + Round(HeadWidth*xNormalUnitDelta),
       yBase + ROUND(HeadWidth*yNormalUnitDelta)),
     Point(xBase - ROUND(HeadWidth*xNormalUnitDelta),
       yBase - ROUND(HeadWidth*yNormalUnitDelta)) ]);
   Canvas.Brush.Color := OrigBrushCOlor;
end;

Thursday, 11 June 2015

TMemIniFile Vs TIniFile

Ini files have been around for some time now and are very useful for storing small amounts of data, and I use them as an alternative to the registry. Years ago I found that they had been designed with a limit of 64k, however these days it is possible to store more than that. Recently I needed to store a fair bit more than ini files are intended for and after some research decided the way to use them.

There are 2 main classes for reading and writing to ini files TIniFile and TMemIniFile, but which one should you use?

Basically, I found out that TIniFile should be used to write to the file and TMemIniFile should be used to read from the file. A few people mentioned to me to just use the TMemIniFile and you should not use TIniFile, however I believe the TMemIniFile uses an internal hash table which causes writing to the file to be slow. So there it is, use TIniFile to write and TMemIniFile to read.

Friday, 22 May 2015

Delphi increases in popularity

I've been developing software using Delphi for well over 15 years and for at least the last 10 years I hear all about how Delphi is dead and developers are moving to other languages. I have just looked at the TOIBE index which list software development languages by popularity and Delphi has moved from ranking 15 last year to 13 this year, so this shows that Delphi is going to stay around for some time to come.

Obviously these ranking change constantly and it could be next year Delphi goes back down the list, but I do think it says shows that Delphi is not dead and that all the people who have said 'Delphi is dead' for the last 10+ years are wrong.

Tuesday, 19 May 2015

Prefix class with 'I'

The other week I was looking at some code another Delphi developer had written and noticed that he prefixed classes with 'I' instead of 'T'. In my world the prefix 'I' means its an interface, but he argued that an abstract class is an interface and that all abstract classes should be prefixed with 'I'. I disagreed with him for various reasons, one being that Delphi standard classes do not do this.

Does anyone else prefix their abstract classes with 'I' and only use 'T' for concrete classes?  

Thursday, 30 April 2015

How to refresh a treeview control

I recent development I have been working on uses a TTreeview control. The underlining classes that relate to the nodes of the control get updated and I reflect the state of the class as dynamic colour of the node text. This state change can happen every few seconds so I wanted to refresh the treeview at least every couple of seconds.

I read a few articles on how to do this but found the treeview would flicker. The best solution I found was simple, when I wanted to refresh just simply call the 'invalidiate' method, at first this did not work until I set the doublebuffered property to true and now it works really well.

Monday, 19 January 2015

Delphi XE5 App does not work with Android Lollipop

Last year I developed a prototype App using Delphi XE5 and deploying to my Nexus 4. It all worked OK and I was fairly happy with what I had. Last month I updated my Nexus phone to Android Lollipop, the other day I tried to open the App and nothing happened it just did not work anymore. I was a little disappointed and a similar App I had developed using Xamarin still worked fine. I am now not sure how feasible it is using Delphi to develop Android Apps.

Sunday, 27 July 2014

Delphi XE Android App Emulator Problem

I have recently been using Delphi XE5 do produce an Android mobile app. All was looking good until I wanted to run the app, the Android SDK comes with an Android emulator, but when I tried running the App from Delphi with the emulator running I just got a blank screen. When I plugged an Android device in it ran fine, so I thought it must be an issue with the emulator. After a few days of using the device I thought I would look into how to fix the emulator issue. All you need to do to fix this problem is to turn on the 'Use host GPU' option when setting up the device in the emulator, after that it will run, however there is another annoying issue, it is very slow.

Monday, 3 February 2014

Using TObjectList in Generics.Collections

In 2009 a new TObjectList was introduced in Generics.Collections, the old TOjectList was still available in the Contnrs unit, but the new one was more strongly typed and you need to specify the class of object the the list will be populated with. This means a reduction in type casting. Here is an example of how to use it:

TMyListClass = class(TObject)
   private
        FEntries: TObjectList<TMyEntry>;
   public
        property Entries: TObjectList<TMyEntry> read FEntries                write FEntries;

        function GetEntry(aIndex: integer):TMyEntry; 

        constructor Create;
        destructor Destroy; override;         
   end;

implementation

constructor TMyListClass.Create;
begin
    inherited Create;
    FEntries := TObjectList<TMyEntry>.Create();
end;

destructor TMyListClass.Destroy;
begin
    FEntries.Free;
    inherited Destroy;
end;

function TMyListClass.GetEntry(aIndex: integer):TMyEntry;
begin
   // You don't need to typecast
   // Old way 
   // if (FEntries.Items[aIndex] is TMyEntry) then
   //    Result (FEntries.Items[aIndex] as TMyEntry; 
   Result := FEntries.Items[aIndex];
end; 



Monday, 27 January 2014

DevExpress Grid Modify Date Header Filter

In a DevExpress grid I needed to remove options from the filter that is available from the column header. Because the header related to the date column it put in a load of filter options that I did not require. I managed to do this by creating an OnGetFilterValues event:

MyGridView.Columns[MyGridView.GetColumnByFieldName('MY_DATE_FIELD').Index].OnGetFilterValues := cxGridGetFilterValues;

Then I put the following code in the procedure:

procedure MyFrm.cxGridGetFilterValues(Sender: TcxCustomGridTableItem; AValueList: TcxDataFilterValueList);

    procedure DeleteFilterEntry(AValue: Variant);
    begin
        AValueList.Delete(AValueList.FindItemByKind(fviSpecial,               AValue));
    end;
begin
      DeleteFilterEntry(foYesterday);
      DeleteFilterEntry(foToday);
      DeleteFilterEntry(foTomorrow);

      DeleteFilterEntry(foNext7Days);
      DeleteFilterEntry(foNext14Days);
      DeleteFilterEntry(foNext30Days);
end;

There are more filter options that could be removed, it just a matter of looking at the unit that contains the constants.

Tuesday, 7 January 2014

Value and reference types

Value types
Live on the stack. Examples of these types are the simple types like integers, and records

Reference types
Live on the heap. Examples of these types are objects, interfaces and pointers.

Strings are unusual because they live on both the stack and heap, behavior is on the stack and the storage is on the heap.



Tuesday, 31 December 2013

Best Delphi Calendar Planner Component?

I current use a very out of date calendar/planner component in part of the software I develop. I have been looking for something to replace this and can only see 2 possible candidates:

  • DevExpress VCL Calendar
  • TMS Software TPlanner component.
Does anyone know of any other components, or have experience of these and can give advice on which is best?

The requirements for the component are:

  • Needs a month, week and day view, it is possible that this changes to just week view.
  • Different types of entries into the calendar, with different colours.
  • Single day view, with option to specify what is displayed in the header.
  • Need to dynamically create multiple instances of the calendar, or some way of displaying multiple calendars similar to MS Outlook.
  • Drag and drop functionality