Have your PIE and eat it too!

If you consider yourself a developer with principles, then consider this;  the use of TDataSet descendants violates the principle of Encapsulation that is foundational to OOP.

Why?  Well the moment you drop a dataset onto a form and use an event handler, that form is now tied to that dataset.  You might think the fix is to use a datamodule - great idea!  The moment you reference the datamodule from your form, the two are tied inextricably together.  All you have really accomplished is not cluttering up the form with data access components, and introducing the requirement to reference the datamodule in every data related line of code, or start using the dreaded with and lose the ability to mouse hover to inspect code values.  If you think you have separated your data access layer (DAL) from your UI, you have - but only visually.  Want proof? Just try to change your DAL components without changing any behaviors in your forms, or one line of form code.

What are the benefits of OOP?  If you don’t know, then perhaps you’re still writing procedural code.  Delphi’s RAD approach to database development encourages writing such code.  Just because a framework uses objects or components doesn’t mean you have to write object oriented code.  There are 3 pieces to the PIE when it comes to OOP: Polymorphism, Inheritance, and Encapsulation.  Encapsulation enables developers to modify an object in the system without introducing side effects (read code breakage) as long as they don’t change the class interface.  Encapsulation is required to write maintainable code.  No one can remember all the details of a system even if they wrote it, so without encapsulation, breakage is inevitable.

In a typical TDataSet based application, a central datamodule is used and datasets are filtered dynamically to meet specific needs throughout an application.  With the use of data aware controls, the current record of a dataset becomes a significant state to maintain across different forms.  Using field and dataset events further complicates the code base, scattering business logic throughout forms, requiring all methods to be aware of the events used, and why, as well as preserving the call chain if they need to tap into an event.  In short, it leads to an unmaintainable spider web of undocumented dependencies.

Think you are never going to change your DAL so what does it matter?  I guess that means no more Delphi updates for you….

13 Responses to “Have your PIE and eat it too!”

  1. magnoz Says:

    Luckily we chose IBObjects more than 10 years ago. So far it worked perfectly with every Delphi version. But I get your point, Delphi makes it easy to make large projects getting hard to maintain.

  2. Pete Rosario Says:

    No one says you have to use one central datamodule. Some of your (valid) criticisms are ameliorated by using multiple, smaller, decentralized datamodules.

  3. Alister Christie Says:

    I think you are going a bit far with your definition of encapsulation, although it’s very true that Delphi database apps are highly TDataSet focused, and ideally it would be nice to be able to swap out one TDataSet descendant for another - however in practice this doesn’t happen (e.g. upgrading from the BDE or shifting from DBX4 to FireDac).

    However it is possible to make your application completely unaware of the database it’s running on by using TClientDataSet’s in your application, connected to TDataSetProviders in another application i.e. a multi-tier application. Although one could argue that you’ve just shifted the problem from one application to another. There are also 3rd party ORM (Object Relational Mapping) libraries that solve this issue. I wonder if we are going to see Embarcadero release their own ORM, especially now that we have both Live Bindings and Generics.

  4. Leonardo Herrera Says:

    All I read around is “TDataSet is bad, bad TDataSet” yet nobody provides a real alternative to it, when it comes to browsing and in-place editing of data. The so-called alternatives are so much more expensive than the cost of the unmaintability a standard application provides.

    The reason I think persistent frameworks haven’t really catch up in the Delphi world is that they are so hard to get right, and that nobody has ever bothered to write a real-life how-to document (or book) on migrating standard classic client-server applications to the latest fancy ORM framework.

  5. Larry Hengen Says:

    @Leonardo,

    With generics and increased reflection capabilities in the newer versions of Delphi, there are now more OPF/ORM frameworks than ever for Delphi. Delphi.About.com had an article enumerating all the solutions available to Delphi developers, one of which is my own hcOPF framework freely available on sourceforge.net. hcOPF supports versions of Delphi as early as D7, and I have been using it for the last 2+ years in a Delphi XE/XE2 project with ~300,000 lines of code. So your argument about cost, doesn’t stand up, although I would admit that I have yet to document hcOPF properly (but that is in the works).

  6. Larry Hengen Says:

    @Alister,

    While I can understand your viewpoint, often developers do not design client/server code using TClientDataSets. I know of, and have personally participated in many conversions efforts from one DAL to another, so in the real world, it’s more common than you might think. Besides that, my argument against TField and TDataSet event usage still stands. The more complex the application, the more it needs to be written to model the real world, and datasets were not designed to model anything other than a table of values.

    I certainly hope EMBT does not produce their own ORM. Doings so would shift their focus away from things only EMBT can do (read fix IDE and other bugs, make compiler enhancements, and develop FireMonkey). Besides that, there are other OPF solutions already available commercially (TMS Aurelius), and otherwise (hcOPF, tiOPF, etc) so it would be a waster of their resources. OPFs are hard to “do right”. Everyone has their own perspective as to what that means, and even Microsoft has taken several kicks at that can…

  7. SilverWarior Says:

    QUOTE: If you consider yourself a developer with principles, then consider this; the use of TDataSet descendants violates the principle of Encapsulation that is foundational to OOP.

    Why? Based on my understanding of Encapsulation it is used to hide portion of data or functionality. And that is what TDataSet component does. It hides the necessary mechanizms for comunicating with database engine and provides easy interface to actually use it.
    So when you need some data record from a Databse you simly say give me this record and TDataSet takes care about all the needed comunication with database engine (conecting to database, sending login request, sending request for certain database record data, formating that data to be used with desendants components, etc).
    Infact any component or class instead of TPersistent uses principles of Encapsulation. While you can use some functionality of parent calls you can’t change that functionality without either changing the parent calss or overriding some method.

    QUOTE: Why? Well the moment you drop a dataset onto a form and use an event handler, that form is now tied to that dataset.

    As soon as you drop any component onto a Form that component is now tied with the Form. Why? Becouse when you do so Delphi IDE automaticly adds necessary code to make that component an internal component of From.
    Yes it is exactly the same when you are declaring internal components (classes) when designing your own components (classes).

    Anywhay I don’t see why tying TDataSet to Form would invalidate Encapsulation principles.

  8. Larry Hengen Says:

    @SilverWarior,

    Using TDatasets prevents you from encapsulating the business logic that you’re using datasets to implement. TDatasets encapsulate data access, but not data validation or business logic. As a result, you normally have to make changes in multiple places when that business logic changes (especially if you use TDatasets on forms). This violates the DRY principle.

  9. ObjectMethodology.com Says:

    I think the idea is that when you drag-n-drop to a form the TForm class becomes the object that encapsulates.

    Also, which most people don’t seem to get, is that the components that come with Delphi are low-level. You can inherit to create the *domain object classes* that you need.

  10. SilverWarior Says:

    QUOTE: TDatasets encapsulate data access, but not data validation or business logic. As a result, you normally have to make changes in multiple places when that business logic changes (especially if you use TDatasets on forms). This violates the DRY principle.

    Data validation is just another level of Encapsulation. Offcourse implementation of this level soley depends on you.
    You can try to validate data on the spot (where you are procesing it) but this would make maintaiing of such approach extreemly dificult since you have your validation code scatered all over the place.
    Another better aproach (as it was hinted by ObjectMethodology) is to make your custom component which derives from original TDataset.
    Offcourse to do so you will need to learn stuff about creating custom components and working designing classes which is quite big topic.
    So if you haven’t learn this stuff yet I strongly recomend that you do as it will help you to better understand how existing components work and provide you with better knowledge of solving the problems in OOP way which can be usefull ina many fields. Infact I dare to say that after you learn this you will look at your programs in comleetly different way as I do after I have learned this.

  11. Fabricio Says:

    Larry,

    Having made some applications using TDataModules hierarchies and is a timesaver.

    So I had a DM for each *entity* (my english is failing me on the example words, consider it an set of classes in a subsystem) and the visual part of the application had no reliance on a determined RDBMS.

    Just changing right DM and the entity is converted - the GUI wasn’t affected.
    If I needed an validation, to share on more than one GUI form, I refactored it to the entity DM and called from it.
    Having an design-time container to my classes and rules, with design-time properties to config and creating entity-level methods, was a time-saver. Coupling with ClientDatasets, my client app knew nothing about the underlying datastore (even consumed and updated “fabricated” resultsets which are applied on MSSQL by the WS).

    That app was 3-tier based on a desktop VCL.NET client and an ASP.NET WebService as middle-tier.

  12. SilverWarior Says:

    When you make derivate components all the components which relied on the original component from which you derivated will still work unles you would intentionally block acces for some functionality of original component from which you derived (hiding methods or properties).

  13. Daniel Says:

    I go with Larry..

    I work with Delphi more than 10 years, in the past 2 years i learn Java and OOP with good pratices like Law of Demeter, GRASP, GOF, Clean Code, etc..

    Now i bring all of good stuff to Delphi, using Aurelius, Livebindings, etc…

    Now i see how much its wrong work with tables, datamodule, etc..

    Work with Domain Classes is much better!

    Sorry for my poor English!

Leave a Reply