Tony Marston's Blog About software development, PHP and OOP

What is a Framework?

Posted on 10th April 2012 by Tony Marston

Amended on 10th January 2022

Introduction
What is a Framework?
The difference between a Library and a Framework
The meaning of "Inversion of Control (IoC)"
The RADICORE framework
1st version in COBOL
2nd version in UNIFACE
3rd version in PHP
Why is RADICORE a framework?
The generated scripts
Table classes
Component scripts
Extending and Customising
Conclusion
References
Amendment History
Comments

Introduction

In order to do something useful with a programming language it is necessary to write code which calls upon the features available in that language. These features may either be provided in the core language or by add-on libraries. When writing applications which contain large numbers of components, where each component handles a different unit of work from the end user's perspective, the code which is written may be classed as either:

Other words I have seen to classify code in this way are:

The ratio between payload/plumbing or worker/navigation code can also be referred to as the signal-to-noise ratio (SNR).

A programmer will be perceived to be more productive if he can spend more time on the payload and less time on the plumbing. This will require access to additional functions, procedures or components which may be provided by either a library or a framework. These functions are in addition to the core language and provide code which has been written by someone else so that the programmer need not spend time writing his own version. It also saves time in that this pre-written code will (or should) have already been debugged, which is another task that the programmer can cross off his list.

I have used the words "library" and "framework", but what is the difference between the two?

In the world outside of software development a framework is some sort of support structure (e.g. exterior scaffolding or interior frame or chassis) which is used to help build other structures. The framework may be removed after construction, or it may become an integral part of the new structure. When building a software application it is also possible to use a software framework, but most of the "software frameworks" available today do not qualify to use the word "framework" in their names. This is because they do not actually offer any sort of support structure, instead all they are is a collection of loose parts which you have to assemble yourself. In my humble opinion they are libraries, not frameworks. The difference can be summarised as:

What is a Framework?

A definition of a framework can be obtained from several sources:

Definition from Craig Larman

In the book Applying UML and Patterns, which was first published in 1997, the author Craig Larman writes the following:

38.4 What is a Framework?

At the risk of oversimplification, a framework is an extendable subsystem for a set of related services, such as:

In general a framework:

Notice here that he specifically mentions the use of abstract classes, which are part of the framework, to provide invariant or unvarying parts of a logical subsystem. He then says that the framework user (that is, the developer) can extend the framework classes to use, customise and extend the framework services by defining subclasses. The use of abstract classes for invariant behaviour, subclasses for customisable behaviour, and the Hollywood Principle whereby the subclasses receive messages from the predefined framework classes come together and are expanded upon in the section on the Template Method Pattern which says:

38.11 Framework Design - Template Method Pattern

The next section describes some of the essential design features of the Database Brokers, which are a central part of the persistence framework. These design features are based on the Template Method GoF pattern [GHJV95].

This pattern is at the heart of framework design. The idea is to define a method (the Template Method) in a superclass that defines the skeleton of an algorithm, with its varying and unvarying parts. The Template Method invokes other methods, some of which are operations which may be overridden in a subclass.

Thus, subclasses can override the varying methods in order to add their own unique behaviour at points of variability.

The Template Method pattern illustrates the Hollywood Principle - Don't call us, we'll call you.

The Hollywood Principle is now formally referred to as Inversion of Control.

Definition from the Gang of Four

In the book Design Patterns: Elements of Reusable Object-Oriented Software which was first published in 1994 the authors wrote the following:

A framework is a set of cooperating classes that make up a reusable design for a specific class of software. For example a framework can be geared toward building graphical editors for different domains like artistic drawing, music composition, and mechanical CAD. ..... You customise a framework to a particular application by creating application-specific subclasses of abstract classes from the framework.

In my case my area of expertise is in the building of enterprise applications which are characterised by having web pages at the front-end, a relational database at the back-end, and software in the middle. This allows the user to put data into and get data out of the relational database while executing sets of different business rules. There may be several different business areas (domains) which are covered by the application, such as product data, order processing, invoicing, inventory, shipments, et cetera. Each of these is developed as a separate subsystem with its own database and its own set of tasks, but these subsystems are allowed to communicate with one another in order to share both processing and data.

It goes on to say the following:

The framework dictates the architecture of your application. It will define the overall structure, its partitioning into classes and objects, the key responsibilities thereof, how the classes and objects collaborate, and the thread of control. A framework predefines these design parameters so that you, the application designer/implementer, can concentrate on the specifics of your application. The framework captures the design decisions that are common to its application domain. Frameworks thus emphasize design reuse over code reuse, though a framework will usually include concrete subclasses you can put to work immediately.

Reuse on this level leads to an inversion of control between the application and the software on which it is based. When you use a toolkit (or a conventional subroutine for that matter), you write the main body of the application and call the code you want to reuse. When you use a framework, you reuse the main body and write the code it calls. You'll have to write operations with particular names and calling conventions, but that reduces the design decisions you have to make.

Not only can you build applications faster as a result, but the applications have similar structures. They are easier to maintain, and they seem more consistent to their users. On the other hand, you lose some creative freedom, since many design decisions have been made for you.

Later on it says:

Frameworks are becoming increasingly common and important. They are the way that object-oriented systems achieve the most reuse.

In a later section it describes the Template Method Pattern using these words:

Template methods are a fundamental technique for code reuse. They are particularly important in class libraries because they are the means for factoring out common behaviour in library classes.

Template methods lead to an inverted control structure that's sometimes referred to as "The Hollywood Principle" that is, "Don't call us, we'll call you". This refers to how a parent class calls the operations of a subclass and not the other way around.

Definition from Douglas C. Schmidt

In the article Applying Patterns and Frameworks to Develop Object-Oriented Communication Software (PDF), which was first published in 1997, the author Douglas C. Schmidt writes the following:

2.2.2 The Benefits of Frameworks

Although knowledge of patterns helps to reduce development effort and maintenance costs, reuse of patterns alone is not sufficient to create flexible and efficient communication software. While patterns enable reuse of abstract design and architecture knowledge, abstractions documented as patterns do not directly yield reusable code. Therefore, it is essential to augment the study of patterns with the creation and use of application frameworks. Frameworks help developers avoid costly re-invention of standard communication software components by implementing common design patterns and factoring out common implementation roles.

2.2.3 Relationship Between Frameworks and Other Reuse Techniques

Frameworks provide reusable software components for applications by integrating sets of abstract classes and defining standard ways that instances of these classes collaborate [Components, Frameworks, Patterns by Ralph E. Johnson]. The resulting application skeletons can be customized by inheriting and instantiating from reusable components in the frameworks.

The scope of reuse in a framework can be significantly larger than using traditional function libraries or conventional OO class libraries. The increased level of reuse stem from the fact that frameworks are tightly integrated with key communication software tasks such as service initialization, error handling, flow control, event processing, and concurrency control. In general, frameworks enhance class libraries in the following ways:

The difference between a Library and a Framework

The shortest definition I have seen can be expressed thus:

Libraries provide functions or APIs which are in addition to those provided by the core language. To make use of one of these functions the programmer must write his own code, or wrapper, to call the function, as shown in Figure 1:

Figure 1 - program code as a wrapper to a Library function

framework-01 (4K)

Although this does help reduce the amount of code that a programmer has to write in order to perform a specific task in a single component, it does not help reduce by a significant amount the volume of plumbing code in an entire application which is comprised of many components.

Software Frameworks, on the other hand, are supposed to provide much more than a simple library of callable functions. The WikiPedia definition states the following:

In computer programming, a software framework is an abstraction in which software providing generic functionality can be selectively changed by user code, thus providing application specific software. A software framework is a universal, reusable software platform used to develop applications, products and solutions. Software frameworks include support programs, compilers, code libraries, an application programming interface (API) and tool sets that bring together all the different components to enable development of a project or solution.

It also identifies the following key distinguishing features that separate frameworks from normal libraries:

  1. Inversion of control: In a framework, unlike in libraries or normal user applications, the overall program's flow of control is not dictated by the caller, but by the framework.
  2. Default behavior: A framework provides basic default behavior from working templates.
  3. Extensibility: A framework can be extended by the user usually by selective overriding of a template or specialized by user code to provide specific functionality.
  4. Non-modifiable framework code: The framework code, in general, is not supposed to be modified, while accepting user-implemented extensions. In other words, users can extend the framework, but should not modify its code.

In order to provide these features it would be reasonable to assume the following:

Business-facing enterprise applications, which are not the same as public-facing websites and which have strict security requirements, may also need extra facilities such as the following:

In my opinion anything which calls itself a "framework" should also supply the following in its documentation:

If your framework does not have any of these then it could be a sign that it is not framework after all, but just a primitive collection of libraries which is masquerading as a framework.

A true framework is therefore a structure that provides wrappers for the program code, as shown in Figure 2:

Figure 2 - framework components as a wrapper to program code

framework-02 (5K)

How can such wrappers be provided? By giving the framework the ability to generate components for new tasks (user transactions) from predefined patterns or templates. These generated tasks will be fully functional, but with standard default behaviour. The components will have empty compartments into which additional code can be placed to enhance or replace the default behaviour. In Object Oriented Programming (OOP) the ability to have empty compartments can be provided by defining empty methods in an abstract superclass, which are called as part of the normal processing sequence, so that each subclass can override the empty method with its own specific code. Each of these methods will be visited at the appropriate time in the processing sequence, and any code found will be executed.

If you have something which calls itself a framework, but it does not have a discernible structure, does not have any pre-compiled components, does not generate any wrappers for your code, does not provide any means to execute the code you have generated, and forces you to write a lot of plumbing code yourself, then it is not a true framework but a simple library.

The meaning of "Inversion of Control (IoC)"

Note here that while Inversion of Control (IoC) has come to mean different things to different people, I prefer to use the first definition which can be found in Designing Reusable Classes which was published in 1988. It contains the following:

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

A framework's application specific behavior is usually defined by adding methods to subclasses of one or more of its classes. Each method added to a subclass must abide by the internal conventions of its superclasses.

This principle has come to be known as the Hollywood Principle (don't call us, we'll call you) which was first used in a paper by Richard E. Sweet which was published in 1985. In the books which I referenced earlier, one by the Gang of Four and the other by Craig Larman, they both mention the Template Method Pattern as being the best design pattern for implementing inversion of control. This pattern plays a fundamental role in my framework as I have a single abstract class which is inherited by every Model class. The abstract class contains concrete invariant methods and empty "hook" methods which can be redefined in any subclass.

This is not to be confused with the Dependency Inversion Principle (DIP) which uses different objects instead of different method implementations in subclasses.


The RADICORE framework

The RADICORE framework is not something which was designed out of thin air, it is actually the 3rd version of a framework which I first wrote using COBOL in 1985, with the 2nd version using UNIFACE in the late 1990s and the 3rd version using PHP in 2002.

1st version in COBOL

Up until 1985 the standard method of building a database application which contained a number of different user transactions (procedures or use cases in the UML world) was to have a logon screen followed by a series of hard-coded menu screens each of which contained a list of options from which the user could select one to be activated. Each option could be either another menu or a user transaction. Due to the relatively small number of users and user transactions there was a hard-coded list of users and their current passwords, plus a hard-coded Access Control List (ACL) which identified which subset of user transactions could be accessed by each individual user. This arrangement had several problems:

This arrangement was thrown into disarray in 1985 when, during the design of a new bespoke application, the client's project manager specified a more sophisticated approach:

I spent a few hours one Sunday in designing a solution. I started building it on the following Monday, and by Friday it was up and running. My solution had the following attributes:

In the first version it was necessary for each new project to combine the relocatable binaries of this new framework with the relocatable binaries of their application subprograms in order to create a single executable program file. I later designed a mechanism to avoid the need for combining these two sets of binaries into a single program file and have separate files for the framework and each application. This involved the use of an Extra Data Segment (XDS), a portion of shared memory, which was written to by the framework before it activated the application program which then read the XDS before taking the necessary action.

This framework was on top of a library of subroutines and utility programs which I developed earlier. This included a COPYGEN program which produced copylib members for both the IMAGE/TurboIMAGE database and the VIEW/VPLUS forms which helped reduce common coding errors by mistakes being made when altering the structure of a database table or a VIEW/VPLUS forms file and failing to update the appropriate data buffer correctly. Calling the relevant intrinsics (system functions) for these two pieces of software was made easy by the creation of a set of subroutines for accessing VPLUS forms plus a set of macros (pre-complier directives) for accessing the IMAGE database. All these documents are available on my COBOL page.

2nd version in UNIFACE

UNIFACE is a proprietary language which is a Component-based and Model-driven language which was based on the Three Schema Architecture with the following parts:

The advantage of UNIFACE is that we did not have to write any SQL queries as they were automatically constructed and executed by the Database Driver. The disadvantage of UNIFACE is that these queries were as simple as possible and could only access one table at a time. This meant that writing more complex queries was impossible unless you created a View which could then be defined in the Application Model and treated as an ordinary table.

In UNIFACE you first defined your database structure in the Application Model, then generated the SQL scripts to create those tables in your chosen DBMS. You then used the Graphical Form Painter to create form/report components which identified which entities and fields you wished to access. When using the GFP the whole screen is your canvas onto which you paint rectangles called frames. You then associate each frame with an object in the Application Model starting with an entity. Inside each entity frame you can either paint another entity or a field from that entity. If you construct a hierarchy of entities within entities this will cause UNIFACE, when retrieving data, to start with the OUTER entity then, for each occurrence of that entity, use the relationship details as defined in the Application Model to retrieve associated data from the INNER entity. After painting all the necessary entity and field frames the developer can then insert proc code into any of the entity or field triggers in order to add business logic.

The first version of UNIFACE which I used, (version 5) implemented the 2-Tier Architecture in that each application consisted of a series of form/report components, which combined both the Presentation layer and the Business layer, and a database component. A later version enabled the switch to the 3-Tier Architecture as it allowed all business rules to be defined in a separate service component. It is this service component which accesses the database, and data is transferred to the form/report component by using XML streams.

Another new feature which was introduced was the ability to create Component Templates which could be used as the starting point for creating new transactions. I extended this idea in my PHP framework in the form of Transaction Patterns.

When I was involved in the creation of a web application using UNIFACE it was such a disaster, due to a combination of a bad technical design and the clunky way that web pages were created (refer to The 3-Tier Architecture - some criticisms of Compuware's approach and Using XSL and XML to generate dynamic web pages from UNIFACE) that I decided to ditch UNIFACE in favour of a language which was designed specifically for the generation of web applications instead of having this capability added on (and badly, I might add) at a later date.

3rd version in PHP

I wrote about this earlier in My career history - Another new language.

I decided to teach myself a new language in my own time on my home PC, so I searched for software which I could download for free. My choices quickly boiled down to either Java or PHP. After looking at sample code, which was freely available on the internet, I decided that Java was too ugly and over-complicated and that PHP was simple and concise, and could run under the Apache web server and access the MySQL database. I learned the language by reading the online manual in combination with some books and online tutorials. I quickly satisfied myself that I could use PHP to create XML files which I could then transform into HTML using XSL Stylesheets, so my first task was to rebuild my existing framework using these new technologies.

My previous decades of experience in writing database applications taught me that the database design always comes first, and only afterwards are the individual application components designed to use the resulting database structure. Note that the needs of some application components may require that the database structure be amended in order for the data access to be as efficient as possible.

My understanding of OOP lead me to believe that the resulting software was 2-Tier by default in that after creating a class with methods you must have a separate component to instantiate that class into an object and then call the methods on that object. It was obvious to me that the class existed in the Business/Domain layer while the component which accessed that class existed in the Presentation layer. This is why each table in my database has its own class in my Business/Domain layer. This followed the UNIFACE convention of having a separate entity in the Application Model for each database table. After rebuilding my framework's database in MySQL I then set about creating the first application components.

Initially for each user transaction I created a separate component in the Presentation layer (called a Controller) and a separate component in the Business layer (called a Model). Each Model class contained the business rules for a single table in the database. As there are only four basic operations which can be performed on a database table using the SQL language, those operations being Create, Read, Update and Delete (CRUD), I created equivalent methods called insertRecord(), getData(), updateRecord() and deleteRecord(). A single Model can be accessed from multiple Controllers, thus allowing the business rules for an entity to be described in a single place yet called from multiple places. As each of the above methods actually performs as series of steps, I put each step into a separate sub method. I also decided that instead of defining each table column as a separate property within each Model class that I would have a single property call $fieldarray which would contain the entire contents of the $_POST array. This then removed the need for having separate getters and setters for each table column.

After creating the components for the first table I created components for a second table by simply copying the code. As this meant that a great deal of code was now duplicated I had to find a way to reduce or remove this duplication. My understanding of OOP made the solution obvious - put the duplicated code into an abstract class which can then be inherited by each concrete table class. I then went through each class and moved the duplicated methods into an abstract table class. This then left each Model devoid of any methods except for the constructor which I initially used to define a variable called $fieldlist which contained a hard-coded list of all those columns which exited in that table. This was because the $_POST array could contain non-database fields such as buttons, and I needed to exclude all non-database fields from the SQL queries which I generated.

Initially each Controller had the name of the Model which it accessed hard-coded into it, but this meant that each Controller was tightly coupled (which is considered to be a bad thing) to a Model. I solved this problem by using a separate component script for each user transaction which set up some variables before activating a reusable Page Controller which then uses whatever variables are passed to it.

When I realised that I had to provide the ability to interrupt the standard processing with custom code in each class I recalled a technique which I had read about years before which involved the creation of additional "pre" and "post" processing methods around particular standard methods. I created empty versions of these customisable methods in the abstract table class which could then be filled with custom code within each concrete sub class. This was made incredibly easy as the each table's data was passed around in a single $fieldarray variable instead of a separate hard-code variable for each column. I later discovered that this technique of using "pre" and "post" methods was in fact an implementation of the Template Method Pattern.

I also decided to write a standard routine to check that each piece of user data was valid for the column into which is was to be inserted, which forced me to change the simple $fieldlist into a $fieldspec array. Initially I had to hard-code this array into each table class, but as this was tedious and repetitive I decided to bite the bullet and create a Data Dictionary to do this task for me. Unlike the Application Model which existed in UNIFACE where you had to first define your table structures in the Application Model and then generate SQL scripts to update your DBMS I decided to do the opposite by having one procedure which imports a table's structure from the DBMS into the dictionary followed by a second procedure which exports that information into a table structure file. This closely resembled the COPYGEN program which I designed some 20 years earlier. I also made this procedure create the initial version of the table's class file.

I also found it tedious and repetitive to go through a whole series of manual steps to create the component scripts for each user transaction as well as adding the relevant entries to the MNU_TASK and MNU_MENU tables in the framework's database, so I automated this task by adding another procedure to the Data Dictionary to do everything automatically. This involves selecting a database table and a Transaction Pattern and then pressing a button to have all the necessary scripts created and database updates performed automatically. This means that I can create a new table in my database, then generate the user transactions to view and maintain the contents without having to write a single line of code - no PHP, no HTML, no SQL. All the standard features of each user transaction are provided by the framework, and all the developer has to do is add custom code into the relevant "hook" methods in order to override or extend this standard behaviour.

Some of my critics, of whom there are many, say that the RADICORE framework is only suitable for simple CRUD screens. This clearly shows that they have only taken a cursory glance at my framework and have yet to comprehend the full extent of its capabilities. Using my library of 45 Transaction Patterns I have built a large ERP application which contains over 4,000 user transactions in 20 subsystems. The Controllers, Views, Data Access Objects and basic Models are provided by the framework, and this represents a significant amount of code which the developer does NOT have to write. Being able to produce working transactions by writing less code means that I can be more productive than the average programmer, and being able to produce code faster and cheaper than my competitors is surely a good thing.

Why is RADICORE a framework?

RADICORE is a true framework because it has a structure instead of being just a collection of loose parts, and that structure can be shown in a simple structure diagram and UML diagrams. Other pseudo-frameworks either don't have what can be recognised as a structure, or it is so complicated that it cannot fit into a single diagram.

The distinguishing features which separate a framework from a library are provided in the RADICORE framework as follows:

Note here that these distinguishing features are provided by a combination of reusable Page Controllers which call standard methods on whichever Model they are instructed to work with. Each of these standard methods is an implementation of the Template Method Pattern which defines a mixture of invariant and variant methods. All the invariant methods are defined in the abstract table class and contain default behaviour while the variant or hook methods, which are also defined in the abstract superclass but are initially empty, allow different implementations to be defined in each subclass.

The RADICORE framework contains the following pre-compiled components and supporting databases:

The RADICORE framework is based on a combination of the 3-Tier Architecture with its Presentation, Business and Data Access layers, and the Model-View-Controller design pattern. The Controllers, Views and Data Access Objects are provided by the framework as application-agnostic services. Each individual subsystem or subdomain has its own database which then requires a separate Model class for each table which exists in the business/domain layer. All HTML output is generated from a standard View module which first copies the data, some of which is supplied by the framework and some by the application, into an XML document and then transforms it into HTML using an XSL stylesheet.

This combination of 3-Tier Architecture and Model-View-Controller is shown in Figure 3:

Figure 3 - Model-View-Controller plus 3-Tier Architecture

model-view-controller-03a (5K)

Note that the small boxes in this diagram do not represent single components but types of component, and each type has a different number of alternatives which are chosen at runtime:

The RADICORE framework implements inversion of control by implementing the Template Method Pattern. This is done by providing a single abstract table class which is then inherited by every concrete table class. Every public method is an instance of a Template Method in that some steps are provided by invariant/fixed methods in the superclass while other "hook" methods, which are initially empty and so do nothing, can have different implementations defined in each subclass.

When developing a large application which deals with several different business domains, such as Orders, Invoices, Shipments and Inventory, a novice developer may think that as each domain requires its own separate database and set of user transactions to manipulate the contents of that database that there would be few components in the framework which could be shared by any of those domains. When you consider that it is only the Model classes which should contain the business rules for each domain, this means that every other component - Controllers, Views and DAOs - should be domain-agnostic and therefore sharable. I use the word should as I have yet to see any other framework which offers this capability. In Radicore they are completely sharable. Each Controller can be instructed to communicate with any Model, each View can extract the data from whichever Model it is given and concert it to HTML, and each DAO can be instructed by any Model to communicate with its database.

Using the RADICORE framework it is possible to take a database table's structure, import it into the Data Dictionary, export the class file, generate the task and then run it without having to write a single line of code - no PHP, no SQL, no HTML. When you run the Generate PHP script task in the data dictionary for LIST1 or LIST2 patterns as well as creating the component script and screen structure file it will also create entries on the MENU database so that the task will immediately appear on a menu button, and the task's children, the ADD, ENQUIRE, UPDATE, DELETE and SEARCH screens, will appear in its navigation buttons. The new task can be run simply by pressing its menu or navigation button.

Although the generated tasks start out by being quite simple, the screen structure files can be modified to alter what data is displayed and where it is displayed, and the table class files can be modified to add whatever additional processing logic is required. Each table class inherits all its methods from the abstract table class, and this abstract class contains a series of customisable methods which are deliberately empty. Each of these methods is called at a particular point in the processing sequence, but as they are empty they do nothing. All the developer has to do to have his own code processed at that point in the sequence is to copy the empty method into his table class and fill it with the relevant code. The non-empty method will then be executed instead of the default empty method.


The generated scripts

Table classes

Each table class is responsible for the data validation and processing of business rules for a single table in the database. The table class file, as created by the Data Dictionary, starts off by being very small as all it needs to identify is the database name, the table name and the table structure (column names and data types), which is actually included from a separate file. All the remaining code is inherited from an abstract table class. Each user transaction executes a number of predetermined methods in a particular sequence. All of these methods are defined within the abstract table class, but some of them are deliberately empty and can be overridden in the concrete table class in order to provide custom code.

The generated table class is capable of handling the basic CRUD (Create/Read/Update/Delete) operations on that table, including proper data validation, without the need for any additional code. However, this basic behaviour can be overridden or enhanced to include whatever custom processing is necessary. Just copy an empty customisable "hook" method from the abstract table class, fill it with code, and that code will be executed at a set point in the processing sequence. For example, by default the class will read from and write to a single database table, but this can be changed so that you can read from multiple tables, either by modifying the SELECT statement to include JOIN clauses, or by inserting code to read from other tables after the original SELECT statement has been processed. Writing to multiple tables involves inserting custom code in either the _cm_post_insertRecord() or _cm_post_updateRecord() methods.

Note that the table class exists in the Business layer, so does not construct and execute any SQL queries as this is the responsibility of the Data Access layer.

If a table's structure is ever altered it can be re-imported into the Data Dictionary and re-exported to the application. In this case it is only the table structure file which is overwritten as the class file may contain custom code.

Component scripts

The component script which is generated is actually quite small, usually containing no more than three lines:

The screen structure file identifies which column goes where on the screen, but the actual building of the HTML output is handled by a pre-written and reusable XSL stylesheet. Each controller automatically calls a standard function which extracts all the column data from a table object and inserts it into an XML document which is then transformed into HTML by the XSL stylesheet.


Extending and Customising

While the generated tasks will perform all the necessary functions to view and maintain the contents of a database table, it may sometimes be necessary to override the defaults with some customisations. This can be done in several ways:


Conclusion

In order to qualify for the title of "framework" a product should have the following attributes:

If the product you are using does not have all of these attributes then does it really deserve the title of "framework"?

A proper framework should greatly reduce, or even eliminate altogether, the amount of code that needs to be written to perform standard "plumbing" tasks, thus leaving the developer with more time to spend on the valuable "payload". One of the early users of the RADICORE framework was amazed at how much code he did not have to write.

If you have something which calls itself a framework, but it does not handle all of this "plumbing" automatically, and you have to write a lot of code yourself, then it is not a true framework but a simple library.


References

Here are some newsgroup posts which criticise my pont of view:


Amendment History

10 Jan 2022 Added 1st version in COBOL
Added 2nd version in UNIFACE
Added 3rd version in PHP
11 Jun 2020 Updated What is a Framework? with a reference to a definition from Douglas C. Schmidt.
09 May 2020 Updated What is a Framework? with a reference to a definition from the Gang of Four.
28 Apr 2020 Added What is a Framework? with a reference to a definition from Craig Larman.
16 Apr 2020 Added The meaning of "Inversion of Control (IoC)"

counter