Showing posts with label abstraction. Show all posts
Showing posts with label abstraction. Show all posts

Monday, September 28, 2009

User Authentication Design

Most systems today, in fact, any system today in which a user interacts with a system, will have some kind of user abstraction. Whether that abstraction is the incoming request for application data or a instantiated class that lives on the server, it nonetheless exists. More often than not, the application needs to know who this user is. It can then make decisions about what, if any, data this user can see or modify. This, of course, is authentication.

There are many different approaches taken to implement user authentication. Most web application frameworks have a built-in authentication system. If that authentication system is flexible enough, it will allow for an external authentication system to be used. This is often the route that is taken by any commercial application simply because systems that were designed to authenticate, often do it well. There is no need to reinvent the wheel. Another reason for doing this might be performance.

However, most simple applications, often web applications, need only simple authentication. By simple, I mean they don't need a production-ready authentication system that can simultaneously handle millions of users. This isn't necessary and would be a waste of time. In these scenarios, simple HTTP authentication will be enough to allow the application to behave as required.

Even simple authentication needs to be designed. There are many approaches that can be taken to implement the underlying authentication one of which is a self-authenticating user abstraction. The user abstraction is necessary no matter what and should always be present in any design. The self-authenticating approach means that the authentication activity is performed by the user abstraction itself, with no need to invoke a separate party. The structure of such an abstraction is illustrated below.



Once an application receives a request for authentication to happen, the user abstraction is instantiated. Once instantiated, this abstraction is then passed the necessary data in order to authenticate itself. The result of the authentication is then passed to the controller responsible for instantiating the user. This sequence is illustrated below.



There are obvious benefits and drawbacks to using such an approach. The drawback is that the user abstraction is instantiated regardless of what the authentication outcome is. This is because the authentication can't happen without a user instance. Should a user instance, even if only momentarily, exist if not authenticated? The benefit here is the self containment. There is no need for an external authentication system since the user is able to state to the system whether or not it is who it says it is. Of course, this may not even be a good thing. An authentication system may be a desired design element.

Thursday, August 20, 2009

Platform Independence In Dynamic Languages

In today's modern computing world, more and more applications are built in dynamic languages. These languages offer more platform independence than compiled languages do. This is because dynamic languages run inside a virtual machine that was compiled for the target platform. The end result is applications that offer more platform independence. Dynamic language virtual machines are becoming more prevalent on end-user systems these days. For instance you'll find a Java virtual machine just about everywhere. In cases where the virtual machine does not exist on the target platform, the virtual machine can be distributed along with the application. The main benefit of course is that once a dynamic language virtual machine is installed, the machine handles the majority of the platform dependent operations.

There are obvious limits to how independent a given application written in a dynamic language can be. This mostly depends on what the application does. Applications that use only the most fundamental features of the language have any chance of being truly platform independent. Realistically, a given application is going to want to take advantage of libraries or modules offered by the language in order to provide better functionality. For instance, most dynamic languages will provide a operating system module for low-level system operations. Not all functionality of this module will be supported on all platforms and there may be subtle behavioral differences in the operations supported across all platforms. It is the responsibility of the application to handle these scenarios.

The best way to handle these platform dependent anomalies is to define platform specific abstractions. This abstraction is illustrated below.



The base Controller class is abstract. It should never be instantiated. Only one of its children, WinController or LinuxController, should ever be instantiated. Therefore, the base and its children should only define functionality that differs among supported platforms.

Tuesday, July 7, 2009

Leaky Abstractions

In an interesting entry, the issue of failed software abstractions is brought up. The title of the entry may be slightly misleading, stating that all abstractions are failed abstractions. In the majority of cases, I would agree that a given abstraction is destined to fail at one point or another. However, there are certain abstractions that can't fail. These are the types of abstractions that are miniscule by comparison with everyday abstraction a developer is likely to see. They are less likely to fail simply because of their small size. Attempting to stuff the entire world into a single class is infinitely stupid and should be avoided, obviously. That being said, there are small abstractions that fail as is illustrated in the entry.

The entry also discusses "leaky" abstractions. What exactly is a "leaky" abstraction? In object-oriented software development, a leaky abstraction can be viewed as an abstraction that doesn't hide underlying problems or inconsistencies. One of the key principles of object-oriented software development is encapsulation, or information hiding. So, it is not unusual for a given high-level software abstraction to use low-level functionality under the covers. This is exactly where the leaks can occur. Technology is almost never perfect, especially when attempting to design code that will run on multiple platforms. If this isn't taken into consideration, leaks will occur. A developer could design the worlds most pristine abstraction that could end up leaking because he didn't consider a subtlety in the underlying technology encapsulated within the class.

The entry uses an object-relational mapper to illustrate an abstraction leak. This has become a popular abstraction for developers in recent years and the problems caused by it are apparent across most if not all software that provides this technology. This particular problem would be an example of a solution domain abstraction leak since the abstraction applies to any business domains using it. One may argue that there exist countless stable software solutions that use object-relational mapper technology and I would agree. I would argue that these projects also had to implement there own niche object-relational mapper abstractions on top of the third party packages just to make them functional. And there is nothing wrong with this because these projects now contain abstractions that do not leak. If this additional abstraction layer weren't implemented, it is nearly impossible to discover where the problem originated, hence the term leak. These types of abstraction leak bug-hunting sessions aren't all that different from memory leak bug-hunting sessions.

In addition to the solution solution domain abstraction leak category, which the object-relation mapper problem falls under, there can also be leaks associated with problem domain abstractions. If the underlying business logic is not fully realized by the abstraction, it can leak in mysterious ways. Abstraction leaks can be as serious a bug as a memory leak and be just as difficult to locate and correct. The main difference being that once a memory leak has been fixed, it is fixed. This is a low-level solution domain issue. Fixing an abstraction leak can have adverse effects on the abstraction itself since the quality of the abstraction matters. It is less-than-ideal to patch an abstraction. Especially in the problem domain.

Wednesday, May 13, 2009

Two Ways to Visualize Method Invocations

Analogy making lies at the heart of object-oriented software development. Developers make analogies to objects in the domain of interest to yield software objects. It is this concept of classes and instances of these classes that make object orientation such a powerful paradigm. When designing object-oriented software, it is sometimes helpful to visualize more than one abstraction for any given solution. As any developer knows, there are several ways to implement a given solution. The act of visualizing different approaches to these abstractions will often yield something in between. The same idea can be applied to understanding and designing the behavioral aspects of software objects. Designing of the behavior of software objects equates to building methods for those objects and how these methods are invoked. There is more than one way to visualize these method invocations.

Method invocations can be visualized as the sending of a message. In this abstraction, the instance that makes the invocation can be thought of as the sender. The instance that implements the method can be though of as the receiver. The method name and method parameters can collectively be thought of as the message. This approach is illustrated below.



An alternative way to visualize method invocations is as the publishing of an event. In the event publishing abstraction, the instance that implements the method can be thought of as the subscriber. The instance that invokes the method can be thought of as the publisher. The method name and the method parameters can collectively be thought of as the event. This approach, not all that different from the first, is illustrated below.



Which approach is the right one? Either. Since this is simply an abstraction visualization strategy, the right approach is the one that yields the better code, and thus, the better software. However, some developers may find the message method invocation visualization approach to be more useful when designing single, one-time method invocations. The event method invocation visualization might prove more useful when designing a polymorphic method invocation over a set of instances.

Tuesday, March 17, 2009

ECP three-level machine abstraction

The Enomaly Elastic Computing Platform is a platform for managing distributed virtual machines. Therefore, we need some type of abstract representation of this concept. This requirement isn't really any different from any other software problem. There is a problem domain which contain concepts unique to that domain. Developers will then try to capture what that concept represents in that domain by creating an abstraction. By creating abstractions in this manor, we lower the representational gap between the domain and how concepts in that domain are realized in the solution. In the case of ECP, there is a real need to represent the idea of machines.

In any given software solution, the abstraction created by developers may be a very simple, single layer abstraction architecture or there could potentially be several layers within the architecture, yielding an extremely complex architecture. In the latter case, without a well thought out design, we start to lose the value that creating an abstraction brings in the first place. Sometimes, when dealing with a large abstraction, further dividing this abstraction into layers can help to better understand what you as a developer are actually implementing. Often, the abstraction design is further complicated by constraints imposed by the system or framework within which we are developing. Rationale, interfaces, and consistency in general, need to be taken into consideration when constructing a layered abstraction architecture.

To implement the machine abstraction, ECP uses a three-level approach to realizing this abstraction. In this architecture, each level is a class that realizes a different level of the "machine" concept, and for different purposes than other layers. In this implementation, the three levels hierarchical. At the top level, we have a class called ActualMachine which implements several methods for invoking machine behavior. The next level contains a class called DummyMachine that inherits from ActualMachine and doesn't do much. Finally, we have a Machine class that can store persistent data to the database. Hierarchically, the DummyMachine and Machine classes are at the same level since they both inherit from ActualMachine. In this discussion, however, the levels aren't necessarily based on the class hierarchy but rather based on the rationale behind each class.

The ActualMachine class is meant to most closely represent the concept of "machine" in the context of ECP. The same symbolizes that this is the underlying machine, not a Python object. Obviously, instances of ActualMachine are Python objects but when using these objects, we are more interested in what the underlying technology. This class is where all the behavior for the machine concept is defined. This class doesn't define any data attributes.

The DummyMachine class is exactly what the name implies; a dummy. The class simply defines a constructor that allows attributes to be set. Also, the class inherits all the behavior from ActualMachine. Instances of DummyMachine can set attributes in the constructor and invoke behavior provided by ActualMachine.

The Machine class provides persistence for the machine abstraction in ECP. The class also inherits behavior from the ActualMachine class. Machine functions similar to DummyMachine in that they both provide the same behavior. The difference between the two is that DummyMachine stores attributes in memory while Machine stores attributes in the database.

The rationale behind this architecture is that we want to be able to instantiate machine instances while not affecting the database. The opposite is also true; we need to be able to instantiate machines that will have an immediate effect on the database. Within the context of the ECP RESTful API, machines that are not stored on the local machine (they are retrieved from another ECP host), will need to be instantiated. That is, we want to have an abstraction available to use once the remote machine data has arrived. This can be done by using some primitive data construct such as a list or a dictionary, but by doing this we lose the machine concept. The behavioral aspect of the machine concept is gone because you can't tell a dictionary to shutdown.

There are still several limitations to this approach. For instance, not all ActualMachine behavior will be supported by the DummyMachine instances that are created. This is simply a limitation of the three classes and their inter-relationships. It is still an improvement over representing domain concepts using primitive types. We give ourselves more control in the three-level architecture over what happens when requested behavior cannot be fulfilled. The DummyMachine layer is an example of mixing the problem domain with the solution domain. The class came into existence because the solution demanded it. But this design allows for the behavior provided by the machine instances to still behave like "machines" without conforming too much to the solution constraints.

A similar approach is taken in ECP with other abstractions such as packages. The architecture hasn't been fully implemented for every abstraction within the platform. It will hopefully prove to add some balance between constraints and offered functionality.

I'm sure this approach prove useful in many other application areas. As objects become more and more distributed, we'll need a better way to represent their data when used locally while preserving the behavior of that object.

Friday, March 13, 2009

How Pylons connects to the ORM

The Pylons Python web application framework manages database connections for any given application written in the framework. It does so by using a PackageHub class for SQLObject. This is actually similar to how TurboGears manages database connections. The database functionality for the Pylons web framework is defined in the database.py module. This are slightly different for SQLAlchemy support in Pylons. In order to provide SQLAlchemy support, Pylons will attempt to define several functions SQLAlchemy requires to connect to the database.

Something I find a little strange is the way the support for both SQLAlchemy and SQLObject is handled by Pylons. Pylons will attempt to import SQLAlchemy and handle the import error. However, Pylons will always attempt to import SQLObject and will not handle an import failure if the library isn't installed on the system. For instance, the following is a high level view of how the database ORM libraries are imported in Pylons.
There is a slight asymmetry here. At the very least, I think SQLObject errors should be handled as well. But what would happen in the event that there are no supported ORM libraries available for import? That would obviously leave us with a non-functional web application. A nice feature to have, and this really isn't Pylons-specific, is the ability to specify in a configuration sort of way, which ORM library is preferred. The database.py module could then base the imports on this preference. For instance, illustrated below is how the ORM importing mechanism in Pylons might work if the ORM preference could be specified.

Here, the flow is quite simple. We load the configuration data, check which ORM was specified and attempt to import it. On import failure, we complain about an ORN not being available. Of course, we will most likely want a default ORM preference if one is not provided. I think that would provide a much cleaner design than basing the ORM preference on what can be imported. There is certain enhancement functionality in which we can base the availability on the fact that the library can be imported. Such as a plugin for a system. But, these are only enhancements. We can't really make these assumptions about a core component like a database connection manager.

The SQLAlchamy connection functionality in Pylons has no notion of a connection hub. There is absolutely no problem with this. The functions are what is needed to establish a connection to SQLAlchemy and they work. For illustration purposes, lets make a new pretend class that doesn't exist called SQLAlchemyHub that groups all SQLAlchemy-related functions together. The following is what we would end up with when visualizing the database.py module.

It is important to remember that the SQLALchemyHub class isn't real. It is there to help us visualize the SQLAlchemy abstraction within the context of the module.

Saturday, March 7, 2009

ArgoUML doesn't support abstraction dependencies.

In the UML, an abstraction relationship shows that one element is an abstraction on another. This is rendered as a dependency relationship stereotyped as abstraction. This is not an available stereotype for dependency elements in ArgoUML 2.6. The abstraction dependency doesn't necessarily need to be attached to the dependency directly.

The UML defines three predefined stereotypes in which the abstraction stereotype is attached to. These are derive, refine, and trace. However, when modeling dependencies, these stereotypes aren't available either.

So, if you have a need to specify these types of dependencies in your models, I wouldn't recommend ArgoUML (although I would recommend it for other UML modeling purposes). In fact, I don't even think Umbrello or Gaphor will get this right.