Showing posts with label Object-Oriented Principles. Show all posts
Showing posts with label Object-Oriented Principles. Show all posts

Wednesday, February 22, 2012

Choosing good abstractions

Coupling and cohesion
The efficiency of the programs you design depends on how well you choose the classes they use.

Well-abstracted classes lead to programs that are easy to maintain and extend. However, if you choose bad abstractions, you could end up with an error-prone application that doesn't model the problem domain effectively.
Simplicity is the key to choosing good classes.

The classes you choose should capture only one key abstraction. If a class performs two functions, then you should split it into two separate classes, each with its own function.
There are five key concepts you should consider when choosing classes:
  • coupling
  • cohesion
  • sufficiency
  • completeness
  • primitiveness
These allow you to measure how well-designed classes are.
Coupling refers to how tightly connected classes are. If a class is very dependent on other classes to carry out its functions, then it is strongly coupled.
Strongly coupled classes can lead to problems in program design. Because such classes are interdependent, they are difficult to extend, and bugs in one can affect others.

This interdependency can also make it difficult for other programmers to understand programs that contain such classes.
To perform adequately, classes must be connected to other classes. But you should choose a level of connection that doesn't make one class too dependent on another.

This is particularly important when you design for inheritance.
Though inheritance is a form of coupling, it can make programs easier to design and extend.

So when choosing classes, you need to balance the requirements of inheritance against the need for weak coupling.
Cohesion is a measure of how closely related the elements in a class are. These elements are the states, behaviors, and functionalities of the class.
A class whose members are simply grouped together and have little in common is only coincidentally cohesive. Such a class might have two unlinked functions, or it may group object states with unrelated object behavior.

Because its elements do not cohere to realize a single purpose, such a class is confusing, difficult to use, and often too complex to implement.
Let's say you used a class named BankEmployee to model how bank staff are paid and how they interact with customers.

Since the elements of these separate functionalities are unrelated, this class would be a bad abstraction.
Classes with good cohesion are well defined and contain elements that properly belong together.

Such classes are easy to understand and use because they serve a definite purpose – they have functional cohesion.

Sufficiency, completeness, and primitiveness
Sufficiency, completeness, and primitiveness are related principles.

A good class needs to balance the requirements of each of these principles before it can function well.
  • Sufficiency
  • Completeness
  • Primitiveness
Sufficiency
Sufficiency is the characteristic that ensures classes are able to function effectively.

You must create classes that contain all the attributes and operations necessary to perform the tasks you require of them. So if you create a Car class, you must include an accelerate operation – otherwise no car object will be able to change speed.

Sufficiency means that you should include only those attributes and operations that are needed for the system you are modeling.

If you want to create a class that models a typical car, you need to specify things like its speed and model.

But you don't need to specify what type of suspension the car uses – this would complicate the class without adding any important functionality to it.

Whether a class is sufficient or not depends on its purpose and on the level of detail it is intended to capture.

If you need to represent something in great detail, then you might have to include specifications that would not be needed in a more abstract model.
Completeness
Completeness determines whether the interface of the class captures all the behavior of the class.

The attributes and operations you give to a class must be general enough to allow other classes to communicate with it.
Primitiveness
Primitiveness means that you shouldn't make a class unnecessarily complicated. It is a principle that lets you control a large spectrum of behavior with just a few operations.

For any class, you should choose operations and attributes that are very basic.

To create more complex class states, you can simply change the values of the attributes you have chosen.

Or you can combine simple operations to create complex ones.

Let's say that in the Car class, you want to model the way gear changes work.

A vertical rectangle with three compartments depicts the Car class with the class name, Car, in the first compartment. The second compartment contains the attributes which are: engineSize, color, model, maxSpeed, currentSpeed. The third compartment contains the operations which are: accelerate(), brake(), turn(), getSpeed(), shiftUp(), shiftDown(), shiftUpTwoGears().

You will need to create a shiftUp operation, but you won't need a shiftUpTwoGears operation. You can simply use the more primitive shiftUp operation twice.

Primitiveness is very closely related to completeness.

To make a class complete, you need to account for all its behavior. But by defining this with primitive functions, you ensure that you don't have to list every possible permutation of a class's behavior.

The level of primitiveness you choose depends on how you want your program to perform.

If you need a program to run very fast, it may be better to use a complex function once rather than using a primitive one several times.

Summary

Classes should capture only one key abstraction. Classes that depend on other classes to function are strongly coupled. You should choose classes with weak coupling, because this makes programs easier to extend, maintain, and debug. Classes should also be functionally cohesive.

To be sufficient, a class must have enough attributes and operations to capture all the relevant states and behavior of its objects. You should make the set of these properties sufficiently complete to allow the purpose of the class to be easily understood, but you should also try to keep the class as simple as possible. You can do this by observing the principle of primitiveness.

Objects, classes, and UML

Classes in UML
A class is represented in UML by a rectangle with three compartments. These are
  • Class
  • attributes
  • operations()
Class
The first compartment in the UML diagram contains the class name, which UML recommends is centered and in bold. It also contains general properties like package owner or stereotype.
attributes
The second compartment contains the class's attributes. Attributes are features within a classifier that describe a range of values that instances of the classifier may hold.
operations()
The third compartment contains the class's operations. Operations are services that can be requested from an object that affect its behavior.
UML recommends that the names of both attributes and operations are left-justified and in plain type.
When representing a class, you can choose to show
  • attributes but not operations
  • operations but not attributes
  • attributes and operations
Or if you don't want to show any of a class's attributes or operations, you can simply represent it with a plain rectangle.
The names you choose for classes should come directly from the vocabulary of the problem domain. This helps you to understand what each class should do, and how it should relate to other classes.

If you have difficulty in naming a class, this can mean that it is poorly defined, or serves no useful purpose and should be discarded.
The following is a standard UML convention for naming classes.
Class names should
  • be singular nouns
  • start with an uppercase letter
Although not a UML recommendation, if you need to give a class a name that uses more than one word, you should
  • push the words together
  • capitalize the initial letter of each word
There are no strict UML conventions for naming attributes and operations. But it is common practice to push together words in any attribute or operation names that use more than one word.

The initial letter of the first word in such names is not usually capitalized, but the initial letter of each word after the first is.


Objects in UML
The UML notation for objects is very similar to the notation used for classes. Just like a class, an object is represented with a rectangle – with or without compartments.
But in contrast to a class name, an object name is always
  • underlined
  • in plain type
An object name can include the name of the class to which it belongs. In this case the class name follows the object name, and the two are separated by a colon.

For example, suppose the object BankTeller belongs to the Employee class. So its object name is bankTeller:Employee.
You can represent an object whose name or particular identity is irrelevant or not specified.
You do this by
  • underlining the class name
  • putting a colon before it
In a payroll system for a bank, it wouldn't be necessary to specify whether objects involved with the system were tellers or managers.

Such objects could be represented anonymously, with just their class name. So you would name each of them :Employee.


Class and object diagrams
Class diagrams are one of the most important types of diagram in UML.

This is because they provide a clear view of the relationship between a proposed system and its problem domain. Also, they can be used in conjunction with many development tools to generate skeleton code (the definition of the classes).
Class diagrams show only the static structure of systems.
They can be used to show
  • single classes
  • large groups of classes
  • relationships between classes
But they do not show the dynamic behavior of classes or the sequence of steps needed for a class to complete a use case.
Class diagrams can depict various levels of abstraction in a system.

They can give a high-level view by showing packages, which are groups of related classes handling specific parts of system functionality.
Or they can focus on the precise details of a single class's attributes and operations.
Class diagrams show a general overview of a system.
Object diagrams are very similar to class diagrams – but they provide a view of what a system looks like at a particular point in time, when classes are instantiated. They are useful for showing concrete examples of how a system functions.

Summary

Objects and classes are both represented in UML by a rectangle. A class has three compartments in the rectangle to show its name, attributes, and operations. An object has two compartments: one for the name and one for the attributes and their values.

Class diagrams show the static structure of classes, while object diagrams depict how classes look when instances are created. Class diagrams can show single classes, relationships between classes, and packages.

In UML, it is recommended, but not mandatory, that class names should be in bold, be without spaces, and have the initial letters of each word capitalized. Object names should be underlined and in plain type.

State, behavior, and identity

Objects
Much of the power and flexibility of modern software analysis and design derives from its use of objects.

The use of objects allows designers to create programs that are more easily maintained and extended. And it makes it easier to design very complex or large-scale programs.
The advantage of using objects in programming is that they allow us to model the real world more accurately.

Procedural programming represents the world as a series of processes acting on data.
However, object-oriented programming represents it the way we really see it – as a set of objects interacting with each other.
Use cases and problem statements allow you to define the problem domain you're analyzing.

After use cases and problem statements have been created, you need to model how the problem domain actually behaves.
By using objects, you can translate features of the problem domain into conceptual models that can be used to create software.

So creating objects is usually the next stage in a design project after use cases or problem statements have been drawn up.
An object is a distinct entity that represents something significant in the problem domain.

It has clear boundaries. And it has a well-defined behavior and a definite purpose.


Classes
Classes are the moulds or templates for the creation of objects. They set out rules for how the objects that they contain may behave.
Classes are part of the abstract structure of a program, and are created when the program is created.

But objects are created only when a program runs. In contrast to classes, they exist at a definite point in time.
Grouping objects into a class allows you to simplify the problem you're modeling.

Classes create an abstract representation of the world, letting you discard unnecessary details.
A class includes objects with similar
  • properties
  • behavior
  • relationships to other objects
The way you group objects into classes depends on the problem you're modeling.

Different problem domains have different requirements, even when they contain the same objects. You will need to create specific classes that suit the needs of each problem domain.
For example, the following are objects relevant to a bank application domain:
  • bank teller
  • manager
  • customer
  • ATM
  • Computer
  • mainframe
To model different aspects of the bank, you need to create classes that will capture the essential features of the problem domain.

So a class named ComputerSystem will capture the essential features of ATMs, computers, and the bank mainframe.
Alternatively, you could create separate classes for each of these objects if you wanted to analyze the problem domain in more detail.
Let's say you want to model the way users interact with the bank computer system.

You can create a class to model the behavior of bank tellers, customers, and managers. And all of these things will be instances of the ComputerSystemUser class.
You may need to rearrange the classes you've chosen if you want to model other aspects of the bank. For example, you might create a class named Employee to model the roles of staff in the bank.
But you would need to split this into an Employee class and a class named Manager if you also wanted to model the bank's chain of command.


State
All objects have three essential features:
  • state
  • behavior
  • identity
An object's state is defined by the attributes of the object and by the values these have.
An attribute is a feature of an object, which distinguishes it from other kinds of objects. For example, one of the attributes of any car object is that it is capable of movement – it has a speed.
An attribute is usually something static. This means that it cannot be removed or altered without changing the essential nature of the object to which it belongs.

For example, a car that could never be moved would not be a typical car.
Though attributes themselves are usually static, the value an attribute can have is usually dynamic.

So you can alter the state of an object by changing the value of its attributes.

Behavior and identity
You can model the states of a system by defining the states of the objects that represent it. But to capture the complexity of real world problems, you also need to consider how these objects behave.
Objects always exist in some kind of relation to other objects.

Sometimes they act on other objects, or even on themselves. And sometimes they are acted on by other objects.
The ways in which an object behaves are determined by its operations. So when an object needs to act on another object – to retrieve data, for instance – it uses an operation.
An operation occurs when a message is passed between two objects, allowing some function to be performed.
For structural and security reasons, some operations and attributes in a class or object are not visible to other classes.

This protects a program against malicious damage. And it prevents data being accidentally deleted or overwritten by other parts of the computer program.
The hidden parts of a class can be accessed only indirectly, through the visible parts of the class.

Together, the visible operations and attributes of a class make up its interface. This is the part of a class that is acted on by operations from other classes or system users.

The three most common types of operations that a class can have are
  • modifiers
  • selectors
  • iterators
modifiers
A modifier operation alters the state of an object.
selectors
A selector operation lets you access the state of an object without changing it.
iterators
An iterator operation allows all the parts of an object to be accessed in a definite order. Object-oriented programmers often create a separate class that is responsible for using the iterator function on an object.
Modifiers, selectors, and iterators are not part of any programming language, but are used to characterize the effects operations have.
Two types of operation are needed to create and destroy instances of a class. These are:
  • Constructor
  • Destructor
Constructor
The constructor operation creates an object and fixes its initial state.
Destructor
The destructor operation destroys an object.
Together, state and behavior define the roles that an object may play. And an object may play many roles during its lifetime.

For example, an object in the bank's Employee class could be involved with the payroll system, with the customer database, or with the command hierarchy.
The functions you require an object to perform are its responsibilities. And an object's responsibilities are fulfilled by its roles – by its state and behavior combined.

So you can capture all the meaningful functionality of an object by specifying its state and behavior.
Objects are characterized by a third feature in addition to state and behavior – identity.

Even objects with the same properties and behavior have their own individual identity. For instance, two blue station wagons that were built in the same year by the same manufacturer are still separate and unique cars.
The identity of an object is independent of its attributes or operations. So an object will retain its identity no matter what values its properties have.

You can, for example, respray your car or fit another engine, and it will still be the same car.

Summary

Objects are representations of abstract or real things. Their use in object-oriented programming is to allow designers to model the world accurately.

Objects represent particular instances of things, and classes represent types of object. Different classes are used for different problem domains.

States are the conditions in which objects exist. An object's state is defined by its attributes. An object's attributes are usually static, and the values of the attributes are usually dynamic.

The term "behavior" refers to how objects interact with each other, and it is defined by the operations an object can perform. There are five kinds of operations: modifiers, selectors, iterators, constructors, and destructors. No matter what its attributes and operations are, an object is always uniquely itself. It retains its identity regardless of changes to its state or behavior.

Wednesday, February 8, 2012

Java as an object-oriented language

1.       Advantages of OOP
2.       Objects and classes
3.       Inheritance and Polymorphism
4.       Summary
Advantages of OOP
Traditional procedural programming breaks up a programming task into a series of steps, or procedures. However, using a procedural approach can make it difficult to divide your program into independent modules.
This means that your programs can be difficult to maintain, extend and test because secondary effects caused by code modifications are more likely, and errors in one section of code can more easily spread to other parts of a program.
In addition, reusable sections of code are more difficult to design and implement.
Object-oriented programming (OOP) is a design to overcome some of the limitations of procedural programming.
Rather than focusing on modelling the procedural of real-life situations in applications, OOP focuses on modelling the objects that appear in them.
This allows you to break programming tasks into independent, manageable modules – which enables you to extend, update, and reuse the code easily.
As a fully object-oriented language, Java benefits from all the advantages of OOP.

Objects and classes
OOP is based on the concept of software objects.
Software objects share certain characteristics with real-life-objects.
Real-life objects have both a state – information associated with an object – and behaviour.
For example, a customer has a state – such as the customer’s a name, address phone number, and credit rating. The customer also has behaviour – such as ordering a product, and paying a bill.
Sometimes the customer’s behaviour can affect its state. For example, by not paying a product on time – an instance of behaviour – the customer alters its credit rating – or state.
Like a real-life object, a software object also has state and behaviour.
A software object’s state is a maintained as data stored in its variables. Its behaviour is implemented in code in its methods.
Because of the way software objects combine variables and methods in the same object, they can easily be used to model real-life objects.
Examples.
Suppose you want to create a software object called Customer that models a real-life customer.
To model a real-life customer’s state, your Customer object can have a variable called name, address, phoneNumber, and creditRating. To model a customer’s behavior, the Customer object can have methods called getCreditRating, orderProduct, and sendBill.
When a Customer object’s sendBill method is called, this method can change the data in the creditRating variable, and so change the Customer object’s state.
OOP’s combining of associated variables and method in the same object is known as data encapsulation.
The use of data encapsulation in OOP is in contrast to traditional procedural programming, in which the data and the code that acts on the data are treated as separate entities.
One the benefits of encapsulation is that the data contained in the object’s variables can be protected from unauthorized access, as you can ensure that it can be accessed only via the methods of the object.
Both real-life objects and software objects have common characteristics and behaviour that they share with other objects.
For example, a real-life customer is one of many customer that share characteristics such as “has a name”, “has an address”, “can order a product”, and “can receive a credit rating”.

However, each individual customer has its own specific state – such as its own name, address, and phone number.
In object-oriented programming, you can create software blueprints called classes that specify all the characteristics that objects of that class possess. You can reuse this template code every time you want to create a similar object.
For example, you can use a Customer class to create objects that represents different individual customers.

Java applications are built using four key programming constructs.
  • Class
    • Classes are the basic building blocks of the Java language. A class is a template that describes the state and behaviour of an instance of that class. For example, a Customer class might specify that all customers have a name and an address, and can get credit rating and receive bills.
    • All Java applications must include at least one public class – a class that is accessible to other classes.
  • Variable
    • A variable is a named space in memory that stores information associated with a class or an object.
    • For example, a Customer object can have variables containing its name and address.
  • Object
    • An object is a specific instance of a class. When you instantiate a class, you create an object of that class. Each object has similar state and behaviour to other instances of the same class.
    • For example, all objects of a Customer class will have a name and address and be able to get credit ratings and receive bills.
  • Method
    • A method specifies behaviour associated with a class or an object. Methods must have a fixed number of arguments and specify a return type.
    • For example, a Customer object can have a method for getting a credit rating or for receiving bills.
This code declares the public class Customer. This class will declare all variables and methods that relate to Customer objects.
Using the public modifier makes the class available to other classes.

Within the Customer class, you then define its variables. You must also assign a data type to each variable.
For example, you define a String data type for the name and address variables because they consist of numerical and alphabetical characters. You define an int data type for the phoneNumber and creditRating variables as they consist of integers only.


You can then add the getCreditRating method to the Customer class. It returns an integer containing the customer’s credit rating.




You can add other useful methods to the class – such as sendBill.

Note: The void keyword before the method name indicates that the method does not return a value.













Inheritance and Polymorphism
As well as encapsulation, the two other key features of OOP are inheritance and polymorphism.
Inheritance and polymorphism allow you to define new classes in terms of other classes.

In its implementation of inheritance, OOP models the nature of real-life objects. For example, a corporate customer and a home customer both types of customers. They share common features – they both place orders and pay bills.
You can say that a corporate customer “is a” customer. That is, a corporate customer has common characteristics shared by all customers. In addition, it also has its own specific characteristics, such as bulk orders.
In OOP, you can use the “is a” relationship to define a hierarchy of classes.

Suppose you create a Customer class that contains the common variables and methods shared by all Customers objects, such as creditRating, orderProduct, and getCreditRating.
To create a CorporateCustomer class – and a CorporateCustomer “is a” Customer – you do not have to create the CorporateCustomer class from scratch. Instead, you can say that the CorporateCustomer class extends the Customer class.
This code extends the Customer class to create the CorporateCustomer class. The new CorporateCustomer class automatically includes all the variables and methods you included in the Customer class. This is known as inheritance.


Because the CorporateCustomer class inherits from the Customer class, it is called a subclass of the Customer class. The Customer class is a superclass of the CorporateCustomer class.

Once you have created a class by extending a superclass, you can create additional methods and variables for the new class that are not in the superclass.
You can also modify the behaviour of the inherited methods. For example, you can write a CorporateCustomer-specific orderProduct method that differs from the orderProduct method of the Customer superclass. This is called overriding a method.
An overridden method requires the same number and types of parameters to be passed to it as the method it override.
Inheritance and method overriding facilitate an OOP concept called polymorphism.
Polymorphism refers to the ability of an object-oriented language to substitute an object of a subclass for an object of a superclass at runtime and to call relevant method of the subclass instead of the superclass method.
Method overriding means that the same call can produce different behaviour depending on the subclass that is called.
This is possible because inheritance guarantees that all subclasses will have the same methods as the superclass.

 
Suppose you have an Employee object that places an order for a customer by using the Customer object’s orderProduct method.
Using polymorphism, you can replace the generic superclass Customer object with a specific subclass CorporateCustomer object.
Because the CorporateCustomer is a subclass of the Customer class and the CorporateCustomer class inherits the Customer class’s orderProduct method, you do not need to change the Employee’s object code for it to call the orderProduct method of the CorporateCustomer object.
This ability to use an object as if it were an object of its superclass, while still using its overridden methods, is the main benefit of polymorphism
It also means that you can write code that doesn’t change when you introduce new subclasses into an application.

Summary
Object-oriented programming (OOP) enables you to create independent modules of code that are easy to manage, extend, and test.
All objects in OOP have a state and behaviour.  Object states are represented by variables, whereas behaviours are represented by methods. Java applications consist of classes which act as templates for related objects, and their variables and methods.
You can extend classes, creating super-and subclasses. Subclasses inherit all variables and methods from the superclass. You can use polymorphism to reference subtype objects as if they were objects of their supertype.