Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

'Making Reuse Fun' instead of 'Making Fun of Reuse' : Page 2

OK, so the COM team enabled us to develop great binary reusable components in DLLs and OCXs. Using all this functionality you develop this amazing component and now you're waiting for the developers to snap it up and start raving. You discover, however, that having a component that can be reused does not guarantee that it will in fact be reused. Amazing capabilities are overshadowed by ease of use and other trivial issues.


advertisement

Reuse Grading System
Reuse means different things to different people.  This is most probably the reason why the word "reuse" is frowned upon by many IT people, especially project managers and the guys higher up in the project tree. These guys normally see reuse as pure "black-box" reuse.  Every developer however has his/her own view about reuse, ranging from the "black-box" view to cut-and-paste of code or even just using the same flowcharts to iron out the logic to be used.  The result is that, too often a piece of code is labeled "reusable", when in practice it takes more man-hours to reuse than to develop from scratch.

At SDT we use a grading system to indicate what level of reuse can be expected from a certain component.  This system serves two important functions.  Firstly it sets a standard when developing a new component.  Secondly it informs the user (or re-user) what can be expected from this component, and how much work will go into getting the component/code integrated into his/her system.

Here is a summary of the six levels of reuse we use to grade components:

  1. Concept Reuse:  The conceptual design of two systems correspond but there is no code reuse, or if code is reused, it does not compile without changes.
  2. Table Reuse:  The conceptual design of two systems correspond and exactly the same table layouts will be used to store data.  There is no code reuse, or if code is reused it does not compile without changes.
  3. Limited Code Reuse: The reuse of a concept to the extent where the conceptual design of two systems correspond and exactly the same table layouts will be used.  The code that is reused comes in encapsulated modules or classes and compiles without any changes.  This code does not address business issues but rather data manipulation and persistence.
  4. Business Logic Code Reuse: The reuse of business logic to the extent where the conceptual design of two systems correspond and exactly the same table layouts will be used.  The code that is reused comes in encapsulated modules or classes and compiles without any changes. The code that is reused addresses business issues.
  5. Binary Reuse: The reuse of business or data manipulation logic to the extent where the conceptual design of two systems correspond and exactly the same table layouts will be used.  Code reuse is on a pure binary level and is language independent.  Objects are exposed via a well defined COM interface. Any object must be able to persist its state to or load it from an XML structure.  All 3 Tier objects must adhere to the design as described in the above mentioned article. Configuration information must be persisted to and from a repository and collected automatically  when needed.
  6. Binary Reuse with User Interface reuse: The same as level five, with an extra DLL added providing access to a reusable user interface.  In the case of our client database sample, this would be a very simple screen allowing you to manipulate client data, plus maybe a search screen etc. A user interface must be supplied to enable input/editing of configuration information.   Note that all user interfaces has to conform to some standards to ensure that the look and feel fits into that of external systems using this interface.
Note: The grading system was developed by SDT over time, so from a binary reuse perspective some of the lower layers may not make sense, but it suites the needs we have at SDT.  I included all the levels here for the sake of completeness, plus to give you an idea of what we see as reusable, and what not.


Binary reuse occurs only at levels five and six.  All components developed by SDT has to be at least on level five. As you can see, we do not really view "code reuse" as reuse.  The reasons for this because reused code has to be tested and maintained in multiple systems.  We aim for binary reuse, and ultimately we want to reuse the user interface as well (level six).

A component on level five or six must behave like a typical "Plug & Play" card you slot into your computer.  The first time it is used in a specific context, the component must collect all the configuration information needed, either from available repositories or by popping up a user interface and prompting the user to supply the information.  This information must then be persisted to a repository where it can be accessed the next time the component needs it, so that in future no user interaction is necessary to get the component configured and up and running.  

Class System
The functionality for making a component as easy to use as a "Plug & Play card" is implemented in clsSystem.  Every project (DLL or OCX) has one system class.  The system class encapsulates all configuration information needed by any class in the project.  My sample projects are very basic, so in the project "Client", this configuration information is limited to a database connection string.  We implement this as the property clsSystem.ConnectionString.  This property initializes itself by calling a function FetchConnectionInfo whenever it detects that no connection string is present. The value of this property is stored in a global named gstrConnectionString.  The value of this variable is globally accessible in the ClientCO project.  This means that all the instances of clsSystem inside ClientCO shares this variable to maintain the connection string 

The property procedures for ConnectionString will look something like this:

Public Property Get ConnectionString() as String
  If Len(gstrConnectionString) = 0 Then Call FetchConnectionInfo
  ConnectionString = gstrConnectionString
End Property

Public Property Let ConnectionString(pstrInfo as String)
  gstrConnectionString = pstrInfo
End Property

The SystemName property
OK, so when ConnectionString is requested, it will be fetched from somewhere.  Nice, but what happens when I have multiple systems using this component and they all use different connection strings? 

A standard property called SystemName is introduced into clsSystemSystemName basically functions as the key for accessing/storing configuration information. The default value of this property is normally hardcoded to the project name, so in the case of sample 1 it will be "Client"  The value of this property is also stored in a global variable.  The code for this property follows:

Public Property Get SystemName() as String
  If Len(gstrSystemName) = 0 Then gstrSystemName = "Client"
  SystemName = gstrSystemName
End Property

Public Property Let SystemName(pstrName as String)
  If gstrSystemName = pstrName Then Exit Property
  gstrSystemName = pstrName
  gstrConnectionString = ""
End Property

Note that changing the SystemName property clears the value of ConnectionString, causing ConnectionString to fetch a new value the next time it is used.  When using more parameters dependant on the system name, they should be cleared here as well.

Reuse Scenarios
The above setup means that we can have Application A and Application B using our Client project, both running on the same client PC and using the same COM+ box.  Application A and B can both run simultaneously on the same client PC, as it will run two different instances of the Client project, each with it's own instance of global data.  Application A sets ClientCO.clsSystem.SystemName to 'A' and configuration information is fetched using key 'A'.  Application B sets ClientCO.clsSystem.SystemName to 'B' and configuration information is fetched using key 'B'.

We can also start up another instance of Application A, change the SystemName property to 'AnotherA' to load other connection information, and thus have more than one instance of Application A running on one client PC, but using different sets of connection information.

Our Contract project also has a clsSystem using the same internal logic as the Client project.  This means that somewhere in the repository there is connection information stored for the Client and the Contract project.  When clsClient is used inside the Contract project, the connection information for Client can be initialized in the following ways:

  • Entries in the repository exists for both Client and Contract. The Client system loads it's own connection information from the repository using the key "Client".  This approach can be used when the two systems is not tightly integrated and normally does not use the same database to store it's data.
  • Entries in the repository exists for Contract.  The Contract system sets the SystemName of the Client system to "Contract".  Both the Contract and Client systems fetches the connection information from the repository on first use of this property.
  • Entries in the repository exists for Contract.  The Contract system sets the SystemName of the Client system to "Contract".  The Contract system fetches the connection information and updates Client's connection information property.

Which of above you use depends on what you want to accomplish and how your systems fits together.  I use the last option in most cases as my systems tend to share the same databases. ()

Note on multiple databases.  Ever tried convincing a client that he needs to restore synchronized versions of 4 different databases?  The moment they understand why, they normally tell you that their backup frequencies differ, and that it is all your problem now.  Rather use a unique table naming scheme.  That way you can put all the tables of all the reusable components making up one system into one database.  This way a backup of a system that links contracts to a client will always be in synch with the tables of the system containing the clients.  Your apps are developed to each use its own database, but when deployed all tables reside in the same database.  The naming convention we adopted is as follows.  Each object in the database of a specific system is prefaced with a three character string.  This applies to tables, stored procedures, views etc.  The client system's prefix of our sample can be 'CLN', while the contract system's prefix can be 'CON'.  That way both can have a table named 'Whatever', but by using the prefix you will never have duplicate names, plus you will know at a glance to which system the table belongs.  The 'Whatever' tables will be named 'CLN_Whatever' and 'CON_Whatever'.

A system class normally has much more properties than the very basic example we have here.  Not all of these properties needs to be persisted though, as these properties can normally be grouped as either:

  • Configuration information that is dependant on the system name and needs to be persisted/fetched from a repository such as the connection information.
  • Information properties that is unique per application instance and has to be created or set every time.  A user id is an example of information that will be unique per application instance.  This information is normally set only once per application.  It does not need to be persisted though, as the next login session will determine the id of the logged in user. 

Maintaining State in the System Class on CO level
In a typical Application using the Client system you would want to set the connection information and system name only once, and then have the system class maintain it for the duration of the application.  All you have to do to accomplish this is to instantiate one instance of ClientCO.clsSystem and to maintain a reference to this instance for the duration of the application (in other words, declare a global variable of type "ClientCO.clsSystem", instantiate it when your application starts up and only destroy it once your application shuts down). The system class is a relatively lightweight class and this should have no significant impact on your memory usage.





Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date