Posts Tagged ‘hcOPF’

hcOPF - Now Supports Design-Time DevExpress Grid Bindings

Monday, March 22nd, 2010

hcOPF is continually being improved as I use it on different projects, and other contributors send me code.  I posted a while ago on the design-time binding for attributes that I added.  Since then, I’ve had a requirement that the current DevExpress grid bindings could not satisfy.

The client required the same grid to be shown on 2 different forms in the application.  You wouldn’t think this would be a problem, but the grid bindings are a global registry keyed on the ObjectClass and ListClass passed to the BindViewToList() method.  You could certainly bind more than one grid to the same list, but when the ThcGridObjectList (or any other cxGrid aware list) instance attempted to locate the TcxGridTableView to which it was bound, it would only find the first view binding registered.  After contemplating extending the grid binding registry key employed to identify the binding, I decided rather than trying to hack a solution for now, it would be best to implement design-time binding.

Ultimately, I would have liked to enabled design-time data binding capabilities to the DevExpress component editor so developers could select an object attribute in the data binding the same way they select a database field in a data bound view.  I’m not even sure if that approach is feasible, and it certainly would not have been consistent with the design-time approach for ThcObject attributes.  Sometimes coding is as much an exploration as a discipline, but I needed to limit the possibility of a throw away design both because I needed to get on with the project work, and design-time coding can be very time consuming since you just can’t step through it in the debugger.  CodeSite is a very useful tool, but you still have to be looking in the right place and sending sufficient information to figure out what is going on.

What I decided to implement was a new ThcMediator descendant that would mediate between a ThcObjectList or descendant and a TcxGridTableView.  Currently, only table views are supported, but it shouldn’t take much to support other view types as well.  I re-factored the ThcMediator, creating an abstract class; ThcMediator and two descendants; ThcAttributeMediator and ThcListMediator.  Then I created a new ThcListMediator descendant (ThcTcxGridMediator) to mediate between a ThcObjectList and the selected cxGrid view.  The ThcObjectList is identified at design-time by it’s Name, which is then used at run-time to attach to the list.  Here is a screenshot depicting how you bind the list to the gridview:

The TcxGridViewMediator manages a ThcDevExpressItemBindingCollection in which you can specify the Item (normally a grid column) to object attribute pairing as shown below:

The downside is that the grid view column must first be created in the cxGrid component editor, before it can be paired with the AttributeName.

This functionality is still considered “in development” as it needs to be tested more thoroughly, but I thought there might be a few interested parties who would want to know of it sooner rather than later.  It certainly beats writing code to bind a list to a grid view, and it eliminates the need to conditionally compile any design-time business objects in order to install them into the IDE without sucking in all sorts of DevExpress code.

Check out the ObjectListMgrDemo application to see it in action (only D2010 and D7 packages are currently up to date).

hcOPF UIObjectBinder gets DesignTime Support

Monday, January 4th, 2010

I used to have some design-time support for the principle objects in the framework, but in my experience component and property editors are non-trivial to write, and the framework was evolving so much that I was constantly breaking all the design-time code.  I decided to back burner design-time support until hcOPF was more mature.  I am happy to announce that ObjectBinding design-time support is now available.

You can now design your objects, install the objects into the IDE, and bind their attributes to form controls without writing a single line of code.  Here is a screenshot of the ObjectListMgr demo showing the design-time support.

Many thanks to Developer Express, not only for donating a license to their DevExpress QuantumGrid Suite so I can continue to support it with hcOPF, and for allowing Richard Morris to share their technique for delegating properties to child objects.  You can find that article here.

UIObjectBinder Design-Time Support

hcOPF Error Indicators

Monday, January 4th, 2010

The JCL and JVCL are a humungous collection of useful components and routines for Delphi.  Kudos to the team that maintains and develops them.  I have used the JCL/JVCL on a number of projects now, and my only complaint is that you can’t really pick and choose what components you want to use.

This came to light once again when I decided I wanted to use the TJvErrorIndicator component with the hcOPF.  I used a single unit from the JVCL and the next thing I knew, I had 3 more packages to distribute; the JCL, JVclCore and JvValidators.

One of the things I like most about Delphi compared to .NET is the ability to create an application with a minimal footprint.  Since I have open sourced hcOPF, I also don’t want to force anyone to adopt any components they don’t wish to use, or deploy anything larger than necessary.

I have looked through the dependancies, and there is really no need for most of the classes I am forced to deploy, they just happen to be in the same unit as something else.  I really wish the Jedi would adopt the SRP (Single Responsibility Principle) for units so I could pick and choose.  As a result, I am thinking about going over to the dark side and ripping out the ErrorIndicator code to create my own unit for hcOPF.  Not exactly an ideal scenario as I cannot contribute back any changes as easily, nor can I benefit as easily from any future enhancements the Jedi make.

Another annoyance is that for some reason the LIBSUFFIX directive is not currently being used for the Jedi packages from D7 up.  As a result I have to change the required package dependancy list for each project for each version of Delphi I am trying to support, rather than simply referencing, for example, JvCore.dcp I have to reference JvCoreD7, or JvCoreD14 etc.  I have reported this issue, and am assured it will be fixed in a future release.

Like other developers I often look to the community for guidelines on naming, code organization, and programming techniques.  What ever happened to dclPackageName${LIBSUFFIX} for design-time packages and PackageName${LIBSUFFIX} for run-time?  What is the recommended standard now?

Designing an Object in hcOPF

Sunday, August 23rd, 2009

hcOPF uses TField like attributes to represent the attributes of an object.  These ThcAttribute descendants provide an object with the ability to Undo any changes made to it’s attributes and notify other Delphi objects when changes have been made.  They are the foundation of the framework, providing a generic, re-usable way of representing object attributes without using simple types and Reflection with it’s limitations and overhead.  In order to specify the attributes for an object and how they’re stored and used, the developer needs to provide the framework with information in some form.  This is Metadata, and although it could be represented in various ways, including XML, in hcOPF it’s currently code.

MetaData is used by the framework to construct an object with the appropriate attributes, based on how the object is stored in an RDBMS.  Every ThcObject descendant must override the Register class method and register it’s metadata with the framework.  A typical Register method would be as follows:

class procedure TPerson.Register;
var
  MetaData: ThcMetaData;
  aDef :ThcAttributeDef;
begin
  MetaData := ThcMetaData.Create;
  with MetaData do
  begin
    with TableDefs.AddTableDef('Person','P',[tpUpdateable,tpInsertable]) do
    begin
      with AttributeDefs do
      begin
        aDef := AddDef('Person_ID',ftInteger,'ID',ftInteger,
                       [apPrimaryKey,apServerGeneratedBeforeInsert]);
        OID := ThcOID.Create('Person',[aDef]);
        AddDef('FirstName',ftString,'FirstName',ftString,[apRequired],30);
        AddDef('LastName',ftString,'LastName',ftString,[apRequired],30);
      end; //with
    end;  //with
  end;  // with
  ObjectRegistry.RegisterObject(TPerson,MetaData);
end;

The lifetime of the MetaData is managed for you, but since the Register procedure is not a callback, you must create the MetaData object.  Metadata contains the definition of the tables where the object attributes are stored and their relationships.  So you begin by adding a Table Definition to the Metadata, specifying the RDBMS table and whether the framework is allowed to insert or update rows in that table.  Then you add Attribute Definitions which essentially specify the column and it’s type in the RDBMS and the corresponding attribute name and type in the OPF.  The OPF is capable of translating database types into internal Delphi types and OPF attributes to some degree.  More on that later…

The final line in every Register() routine should be a call to register the object with the framework.  Namely:

ObjectRegistry.RegisterObject(TPerson,MetaData);

Then somewhere in the project you need to call the Register class method for all objects.  I typically do this in the initialization section of the same unit in which the business object is defined.  In the finalization section you should also call the UnRegister method, although technically this is not necessary since the application termination will reclaim the memory.

initialization
  TPerson.Register;
finalization
  TPerson.UnRegister;

In hcOPF a ThcAbstractFactory descendant is the object that is responsible for persisting a ThcObject descendant. To get a factory instance you request it from a FactoryPool.  All applications must contain 3 core objects:  a FactoryPool, a SQLMapper, and a TransactMgr.  Normally you would drop these components onto your main datamodule and hook them up like this:

The FactoryPool knows about the different factory classes available for persistence.  In this case, IBX has been chosen as the DAL for a FireBird database.  Once the datamodule has been created and the database is connected, you can load and persist objects as you wish.

For a simple example on using the framework with standard Delphi controls see the following.  This application has a rather contrived database structure used to show lazy loading (even though it’s triggered immediately) with a child object.  This example also shows how to display content from two different objects on the same form, and persist them in a single transaction.  The database included was created with Firebird 2.0.  I have included a script as well so you can use Interbase if you prefer.  I also included the EXE in case you want to run the application without setting up the framework to compile the project.

How Delphi Developers Sabotage Delphi’s Future

Monday, July 20th, 2009

I was recently reminded why I dislike TDataSets so much while working on a new client project.  The project is written in Delphi 5 by a developer who was relatively new to Delphi and object oriented programming from what I am told.  The program was developed over a long period, and “evolved” like all applications evolve.  The original developer moved on, and was replaced by a number of remote developers.  Kudos to the company for trying a more modern day approach; distributed development (a topic for another day).

As with many RAD development projects, the application suffers from three major problems:

1) developers jumped into code immediately before addressing design issues

2) business logic is scattered throughout both the form and datamodule code, often with duplication and there is no seperation of data access logic from the UI forms (so much for the single responsibility principle).

3) there is hardly a stitch of documentation in the code, and there are no requirement documents to be had.

The result, it a spiderweb of code.  It’s almost impossible to modify portions of it without introducing bugs, and it takes forever to get a thorough understanding of what the code is doing and when.  There is not even a consistent use of DataSet events like OnBeforePost vs. MasterFields etc for Master-Detail relationships.  Reports are dynamically firing queries to perform lookups on ID columns.  There is no concern for how hard they are pounding the database.  On top of that, they’re using a TDataSet descendant to talk to a proprietary database which means they are so tightly coupled to that database that changing down the road will be a nightmare.  I’ve seen this problem before with paradox tables and lower end RDBMS products that simply did not scale to the clients needs in the future.

Time and time again, I see the same problem biting companies, and reflecting poorly on Delphi as a product and Delphi programmers as a whole.  Companies seem to errantly think that paying a junior developer $20/hr. is saving them money compared to a senior developer who may cost twice as much.  What they don’t seem to understand is the unmaintainable mess they get with junior staff.  It’s usually at that point that the company decides if they have to invest a whole bunch of effort in the Delphi product they would be better off to find a junior .NET developer and re-write the product using a better technology.  The technology was never the problem.  

Now personally, I like many things about .NET, but I don’t consider it to be necessarily better than Delphi.  It’s just another tool in the toolkit, and the results a company gets has more to do with their selection of the programmer who wields the tool.  I still like Delphi because it produces such lean EXEs, has no run-time to distribute, and no delays loading assemblies or jitting code.  For some applications, it just makes more sense.

It’s just really unfortunate that Delphi provides only a RAD framework based on the dataset.  Of course .NET has an even more powerful TDataSet like object, but the .NET community has leveraged the Open Source world including the Java projects and you have an explosion of OPF/ORM options.  The movement towards agile methodologies incorporating various frameworks such as unit tests, mocking, and business objects/ORMs is much more prevalent in the .NET space.  Perhaps if the Delphi community had more activity in these areas, Delphi would be seen as a more cutting edge technology.  It would be even better if more Delphi developers actually used such practices.

The next time before you start dropping datasets onto a datamodule, think about your options.  If you want the application to be around 5 years from now, and Delphi work to be available, think about using an ORM to isolate the application from the database, and encapsulate the data you’re using.  Extend the benefits of OOP to your data, and future changes will be much easier to make.  Look at the frameworks available for Delphi for free.  Even if you’re using an older version of Delphi, there are still lots of options available.  Now, one of those options is hcOPF.  In order to use it on my latest project, I back ported it from Delphi 7 to to Delphi 5.  You will find a Compiler folder with projects for Delphi 5 and 7.  Eventually I will create one for D2007 as well, and hope that others “fill in the blanks” since those are the only versions I am currently using.