Throughout the object-oriented software development process, you're likely to find new and better ways to query object properties. These properties aren't necessarily the defined attributes for the object's class. The methods used to query these attributes are often obvious enough. What about derived properties? These are computed properties and they sometimes go unnoticed until some iteration further down the line.
Is the best approach to adding these newly discovered query methods to add them to the class directly? Probably not because you are adding new responsibilities directly to the class. There are exceptions to not not doing this. For instance, there might be some aspect of the objects that you may want to query that are painfully obvious and were missed for some reason. Also, if the class is somewhat specialized already, the newly discovered query methods may only apply to instances of this class.
When more general query methods are discovered, we can use the inheritance mechanism to extend the functionality of our class. This can go in either direction. You can define a specialized version of your class that is a child of that class. This class would then define the new query methods. The problem with this approach is that rather than being more generalized, you are increasing the specificity level. This is fine if the newly discovered query method are specific to your class. More often than not, however, derived properties that are computed can often be applied to a group of disparate classes. If this is the case, spending the time to develop a highly-reusable query super class may be worthwhile.
Showing posts with label inheritance. Show all posts
Showing posts with label inheritance. Show all posts
Monday, April 5, 2010
Tuesday, March 2, 2010
Not Implemented
What makes more sense with abstract operations in Python, to pass silently or to raise a NotImplementedError exception?
The pass is easy to implement quickly and it is usually obvious to developers reading the code that the operation is abstract. During the execution of the program, if the chain of invocation leads to an empty, abstract method, it could lead to very subtle problems.
The NotImplementedError can at least be handled and some logs might be produced complaining about the lack of an implementation.
The pass is easy to implement quickly and it is usually obvious to developers reading the code that the operation is abstract. During the execution of the program, if the chain of invocation leads to an empty, abstract method, it could lead to very subtle problems.
The NotImplementedError can at least be handled and some logs might be produced complaining about the lack of an implementation.
Friday, October 16, 2009
Python Super Classes
The Python programming language is considered to be an object-oriented language. This means that not only must it support classes, but it must also support inheritance in one form or another. Inheritance is the principle of object-oriented software development that allows developers to say class A "is a kind of" class B.
Not all object-oriented languages support it, but multiple inheritance is another form of inheritance what allows developers to say class A "is a kind of" class B "and is also a kind of" class C. The Python programming language does support multiple inheritance and can support designs that employ the principle when needed.
Opponents of multiple inheritance say that it is an unnecessary feature of object oriented languages and in most cases, they are correct. Not necessarily correct about the fact that multiple inheritance shouldn't be a language feature, but about the design itself. Like anything else, multiple inheritance can be abused and actually hurt the software. Most of the time, it isn't needed and something more simplistic in design terms is ideal.
Consider the following class hierarchy. Here we have a Person class that acts as the root of the hierarchy. Next, we have an Adult and a Remote class, both of which inherit directly from Person. Finally, the Student class inherits from both Adult and Remote. The Student class uses multiple inheritance.
This is an example of where multiple inheritance may come in handy. The Remote class represents something that isn't local. This could be a Student or something else in the system. Since it is required that Student inherit from Adult, it makes sense that it also be able to inherit from Remote. A student can be both things.
Below is an example of this class hierarchy defined in Python. The super() function really helps us here because the Student class would otherwise need to invoke the individual constructors of each of its' super classes. Not only is this less code, it is also more generic of a design. All Student instances base class constructors will continue to be invoked correctly, even as these base classes change.
Not all object-oriented languages support it, but multiple inheritance is another form of inheritance what allows developers to say class A "is a kind of" class B "and is also a kind of" class C. The Python programming language does support multiple inheritance and can support designs that employ the principle when needed.
Opponents of multiple inheritance say that it is an unnecessary feature of object oriented languages and in most cases, they are correct. Not necessarily correct about the fact that multiple inheritance shouldn't be a language feature, but about the design itself. Like anything else, multiple inheritance can be abused and actually hurt the software. Most of the time, it isn't needed and something more simplistic in design terms is ideal.
Consider the following class hierarchy. Here we have a Person class that acts as the root of the hierarchy. Next, we have an Adult and a Remote class, both of which inherit directly from Person. Finally, the Student class inherits from both Adult and Remote. The Student class uses multiple inheritance.
This is an example of where multiple inheritance may come in handy. The Remote class represents something that isn't local. This could be a Student or something else in the system. Since it is required that Student inherit from Adult, it makes sense that it also be able to inherit from Remote. A student can be both things.
Below is an example of this class hierarchy defined in Python. The super() function really helps us here because the Student class would otherwise need to invoke the individual constructors of each of its' super classes. Not only is this less code, it is also more generic of a design. All Student instances base class constructors will continue to be invoked correctly, even as these base classes change.
#Example; Using the super() function.
#Root class.
class Person(object):
def __init__(self):
super(Person, self).__init__()
print "Person"
#Adult class. Inherits from Person.
class Adult(Person):
def __init__(self):
super(Adult, self).__init__()
print "Adult"
#Remote class. Inherits from Person.
class Remote(Person):
def __init__(self):
super(Remote, self).__init__()
print "Remote"
#Student class. Inherits from both Adult and Remote.
class Student(Adult, Remote):
def __init__(self):
super(Student, self).__init__()
print "Student"
#Main.
if __name__=="__main__":
#Create a student.
student_obj=Student()
Labels:
design
,
inheritance
,
objectorientation
,
python
,
super
,
superclass
Friday, October 2, 2009
Polymorphism And Inheritance
One of the main key principles of the object-oriented software development paradigm is polymorphism. Polymorphism in the context of object-oriented design is the ability to define behavior that varies by type, as opposed to varying by interface. For instance, in functional programming design, to invoke different behavior, a different function name is invoked. In object-oriented design, behavior can be invoked on instances by using a single method name. This means that the behavior that is actually executed depends on the type.
Inheritance, another key principle of object-oriented design, plays a big role in implementing polymorphic behavior. Given a class hierarchy, the topmost classes will often define a base interface for behavior. These base methods often go unimplemented. It is the classes that inherit from the base class that are responsible for implementing the behavior. This means that subclasses even further down the inheritance hierarchy can also define this behavior.
Any descendants of the base class can be used in the same context and will behave as expected. Below is an example illustrating the difference between inheriting a method and providing an implementation for it and inheriting an already-defined method.
Inheritance, another key principle of object-oriented design, plays a big role in implementing polymorphic behavior. Given a class hierarchy, the topmost classes will often define a base interface for behavior. These base methods often go unimplemented. It is the classes that inherit from the base class that are responsible for implementing the behavior. This means that subclasses even further down the inheritance hierarchy can also define this behavior.
Any descendants of the base class can be used in the same context and will behave as expected. Below is an example illustrating the difference between inheriting a method and providing an implementation for it and inheriting an already-defined method.
#Example; Polymorphism and inheritance.
#Do imports.
import uuid
import timeit
#Simple person class.
class Person(object):
#Constructor. Initialize the data.
def __init__(self):
self.data={"first_name":"FirstName",\
"last_name":"LastName",\
"id":uuid.uuid1()}
#Return the first name.
def get_first_name(self):
return "first_name_%s"%(self.data["first_name"])
#Return the last name.
def get_last_name(self):
return "last_name_%s"%(self.data["last_name"])
#Return the id.
def get_id(self):
raise NotImplementedError
#Simple manager class that extends Person.
class Manager(Person):
#Constructor. Initialize the Person class.
def __init__(self):
Person.__init__(self)
#Return the manager id.
def get_id(self):
return "manager_%s"%(self.data["id"])
#Simple employee class that extends Person.
class Employee(Person):
#Constructor. Initialize the Person class.
def __init__(self):
Person.__init__(self)
#Return the employee id.
def get_id(self):
return "employee_%s"%(self.data["id"])
#Main.
if __name__=="__main__":
#Employee.get_id() timer.
t_employee_get_id=timeit.Timer("Employee().get_id()",\
setup="from __main__ import Employee")
#Manager.get_id() timer.
t_manager_get_id=timeit.Timer("Manager().get_id()",\
setup="from __main__ import Manager")
#Employee.get_first_name() timer.
t_employee_get_first_name=timeit.Timer("Employee().get_first_name()",\
setup="from __main__ import Employee")
#Manager.get_first_name() timer.
t_manager_get_first_name=timeit.Timer("Manager().get_first_name()",\
setup="from __main__ import Manager")
#Display the results.
print "Employee Get ID: ", t_employee_get_id.timeit(10000)
print "Manager Get ID: ", t_manager_get_id.timeit(10000)
print "Employee Get First Name: ", t_employee_get_first_name.timeit(10000)
print "Manager Get First Name: ", t_manager_get_first_name.timeit(10000)
Sunday, January 4, 2009
Atomic Inheritance
Rather than try to define mega do everything in the world super classes, why not use inheritance to your advantage by defining fine-grained hierarchies?
For example, consider the following class diagram.
Here, we have three classes; Content, User, and HTML. The Content class defines most of the functionality used in this partial design of a CMS. It is a very basic class that defines features common to the content abstraction. The User class is also missing basic features but this is for simplicity. The association relationship between the User class and the Content class is indicative that there is a User instance associated with each Content instance. Finally, the HTML class extends the Content class; HTML is a content type.
Atomic inheritance means that every class in the class hierarchy should be atomic. In this context, atomic is really only a guideline instead of a rigorous design requirement. Traditionally, in object-oriented inheritance, atomic refers to a leaf class. A leaf class is a class which has no children, a class with no further specializations. How then, do we build inheritance hierarchies using only atomic classes.
Again, assuming that the term atomic is only referring to a logical design requirement, it isn't too difficult. Lets take another look at our CMS example. The first change to note here is that both the Content and User classes define a name attribute and name behaviour. A logical atomic class here would be Name. The same example can be seen with the created attribute. Here we could create a new atomic class called Date.
You can hopefully see where I'm going with this. Typically, there is a decent mapping from attributes to atomic classes. Here is the new class diagram after refactoring.
As you can see, in this example of atomic inheritance, we have completely eliminated all attributes and operations from the User class. The class simply ties together two other atomic classes while at the same time reducing the size of the Content class.
For example, consider the following class diagram.
Here, we have three classes; Content, User, and HTML. The Content class defines most of the functionality used in this partial design of a CMS. It is a very basic class that defines features common to the content abstraction. The User class is also missing basic features but this is for simplicity. The association relationship between the User class and the Content class is indicative that there is a User instance associated with each Content instance. Finally, the HTML class extends the Content class; HTML is a content type.
Atomic inheritance means that every class in the class hierarchy should be atomic. In this context, atomic is really only a guideline instead of a rigorous design requirement. Traditionally, in object-oriented inheritance, atomic refers to a leaf class. A leaf class is a class which has no children, a class with no further specializations. How then, do we build inheritance hierarchies using only atomic classes.
Again, assuming that the term atomic is only referring to a logical design requirement, it isn't too difficult. Lets take another look at our CMS example. The first change to note here is that both the Content and User classes define a name attribute and name behaviour. A logical atomic class here would be Name. The same example can be seen with the created attribute. Here we could create a new atomic class called Date.
You can hopefully see where I'm going with this. Typically, there is a decent mapping from attributes to atomic classes. Here is the new class diagram after refactoring.
As you can see, in this example of atomic inheritance, we have completely eliminated all attributes and operations from the User class. The class simply ties together two other atomic classes while at the same time reducing the size of the Content class.
Labels:
architecture
,
atomic
,
design
,
inheritance
,
uml
Tuesday, December 2, 2008
An interface/generalization design pattern
Last month, I wrote about an interface/generalization design pattern. I thought I'd post a diagram which I feel better illustrates the pattern.
Again, to some the pattern up:
Again, to some the pattern up:
- The base interface specifies the contract that all classes who realize this interface must implement.
- The base class which realizes the base interface. This instances of this class will ensure that the expected default behavior of some collaboration can always be carried out in the case that a more specialized sub-class cannot be instantiated.
- You may create a sub-class of the base class which realizes the base interface. This class has the ability to redefine certain methods of the base class while using the default implementation of others.
- You may create a regular class that realizes the base interface. It can be thought of as a child of the base class because it will behave the same as any other children of the base class. The main difference being that this class must implement the entire interface. There are no defaults here.
Labels:
architecture
,
inheritance
,
interface
,
pattern
,
uml
Monday, July 21, 2008
A data pattern
A useful data pattern one can implement is to extract all behavioral features from the abstraction in question, and into a new behavioral super class. This trivial pattern will allow several data implementations of the class inherit the same behavior interface.
For example, here is a simplistic view of this pattern in the context of a blogging system.
Here, we have three classes; BlogBehavior, BlogDB, and BlogMemory. BlogBehavior defines the methods involved with what a blog does. BlogDB defines data access methods for interacting with the database. BlogMemory defines data access methods that will store and retrieve attributes in memory.
Methods defined by instances of BlogDB and BlogMemory may be invoked by methods in BlogBehavior. This would be ideal, because which of the two data instances is being used in the context of the blog system doesn't matter.
The concept of separating abstrations based on storage location, data access technonlogy (same database, different API), is not new. I have witnessed several ocasions, however, where the same behavior is not generalized. I think this is an important practice to keep in touch with when using object technology.
For example, here is a simplistic view of this pattern in the context of a blogging system.
Here, we have three classes; BlogBehavior, BlogDB, and BlogMemory. BlogBehavior defines the methods involved with what a blog does. BlogDB defines data access methods for interacting with the database. BlogMemory defines data access methods that will store and retrieve attributes in memory.
Methods defined by instances of BlogDB and BlogMemory may be invoked by methods in BlogBehavior. This would be ideal, because which of the two data instances is being used in the context of the blog system doesn't matter.
The concept of separating abstrations based on storage location, data access technonlogy (same database, different API), is not new. I have witnessed several ocasions, however, where the same behavior is not generalized. I think this is an important practice to keep in touch with when using object technology.
Subscribe to:
Posts
(
Atom
)