-
Relationships between objects (classes)
- There are several relationships been objects in java:
- Association
- e.g. a Man uses a Pen
- Aggregation
- e.g. a Man has an Address
- Address can outlive Man
- Composition
- e.g. a Man owns a Brain
- Brain dies when Man dies
- Generalisation
- e.g. a Man is a Human
- Abstraction
- Concept rather than Implementation
- Realisation
- Implements Concept
- Dependency
- Change in one affects another
- Associations define a relationship between two objects and can define multiplicity:
- One to one
- Order to Payment
- One to many/many to one
- Playlist to PlaylistItem
- Many to many
- Project to Employee
Associations are implemented as a reference in one class to the other class or in each of the classes.
Aggregation
Aggregation is a special case of association as it has a direction i.e. from master object, a ‘has-a’ relationship where the master object uses the other object, e.g. Customer aggregates Project objects
Composition
Composition is a special case of aggregation with is more restrictive than aggregation where the contained object only exists as part of the master object, i.e. is owned by and the contained object dies when master object dies, e.g. Order and OrderLine.
Abstraction
Abstraction is where we specify the outline but not the details. Details are implemented elsewhere, e.g. abstract class or interface. Abstraction is often used to define commonality. e.g. Project and AdHocProject.
Generalisation
Where one class is a type of another class, Is-a relationship. The specialized class is subclass of a generalized class. Commonality defined in Generalized class and enhanced or overridden in Subclass (specialized class) i.e. Inheritance, e.g. Contractor and Employee.
Realisation
Relationship between an abstraction and a concrete object, i.e. implementation of details specified in abstraction. e.g. a concrete class derived from an abstract class. e.g. DAOTextImpl implementing DAOInterface.
Dependency
A relationship where a change in one object affects the another object. Can be unidirectional or bidirectional. e.g. Post and NoticeBoard.
Association vs Composition vs Aggregation Association Defines a relationship between classes. Composition and Aggregation are types of associations. Composition The Employee is encapsulated within the Company. There is no way for the outside world to get a reference to the Employee. The Employee is created and destroyed with the company. Aggregation The Company also performs its functions through an Employee, but the Employee is not always an internal part of the Company. Employees can be exchanged, or even completely removed. As the employee is injected, the Employee reference can live outside the Company. Dependency The company does not hold the employee reference. It receives an employee reference only to the scope an operation. Company is dependent on the Employee object to perform an operation. Abstraction Defines the basic operations the implementer should adher to. Employee interface lists the general behaviour of an employee Realisation A class implements the behaviour defined by the other class or interface Generalisation A class which is a special form of parent class. -
Software Development
Complex systems cannot be understood/developed as a whole without abstraction. Using the divide and conquer approach we can breakdown problems into Independent sub-systems which are integrated to solve whole problem. In order to so we need to ask ourselves how to partition system into sub-systems, and how to define communication between sub-systems?
“The beginning of wisdom for a [software engineer] is to recognize the difference between getting a program to work, and getting it right” (Michael Jackson)
All abstractions are "incomplete and inaccurate“, their strength lies in the emphasis of important details and rejection of irrelevant ones in current context. Abstractions allow us to postpone decisions concerning implementation for as long as possible rather than prematurely constrain the development space.
Higher levels of abstraction focus on outlining details of a system and are expressed in a language of problem environment (domain). Lower levels of abstraction will be more detailed focusing in on problem solution and can be easily expressed in a programming language. Software development refines an abstraction into a solution.
Refinement
“In each step (of the refinement), one or several instructions of the given program are decomposed into more detailed instructions. The successive decomposition or refinement of specifications terminates when all instructions are expressed in terms of any underlying programming language.
Every refinement step implies some design decisions. It is important that … the programmer be aware of the underlying criteria (for design decisions) and of the existence of alternative solutions …” (Niklaus Wirth)
-
Functional Decomposition
Functional Decomposition is the procedure of taking a complex process and breaking it down into smaller, more manageable parts. The process can continue while each part can be broken down further. This allows us to concentrate on one part at a time essentially refining the problem into smaller, simpler problems and then refining again until you reach manageable chunks, moving us from abstraction to a solution detail. In order to carry this process out we must understand how you determine the decomposition.
Modularity
Software architecture uses modules, this allows us to partition systems into separately named and addressable components which encourages separation of concerns. We can then Integrate these components into a produce solution. There are two main approaches to modularity, the 'Top Down' approach which is a stepwise refinement based on functional decomposition, and the 'Bottom Up' approach which begins with the design/implementation of sub-problems and combines them to solve a bigger problem.
Module characteristics
- Modules should be functionally independent, should focus on a single function, and communicate with other modules through well-defined interface. Modules may well be decomposed into sub-modules which allows us to create a module hierarchy. There are several advantages to modules, They:
- Encourages pluggability, easier maintenance, independent development, reuse
- Supports better testing
- Minimises propagation of error/fault
- Helps to manage complexity
Encapsulation
"Encapsulation is the process of hiding all of the details of an object that do not contribute to its essential characteristics.“ (Booch)
"Encapsulation (Information Hiding). A principle, used when developing an overall program structure, that each component of a program should encapsulate or hide a single design decision... The interface to each module is defined in such a way as to reveal as little as possible about its inner workings.“ (Coad)
Encapsulation allows us to hide the inner workings (code/data) of a module from other modules, allowing design decisions made inside module and hidden from other modules. Well-defined interfaces exposes module’s functionality to the outside world and only communicate what is necessary. This encourages testability, pluggability, independence as well as reducing the “ripple effect” of changes and error propagation.
- On order to properly partition systems we must ask ourselves the following questions:
- How many modules should there be?
- What size should a module be?
- How deep should a module hierarchy go?
- How many interactions should there be between modules?
- What are the best type of interactions?
Module Size
Many guidelines have been postulated for module size. In essence a module should carry out one clear identifiable task where there is no identifiable benefit to breaking the task into sub-tasks. A module that looks too complex probably is and remember the simpler the module the easier/cheaper it is to develop. However, smaller modules mean more modules and more communication between modules which increases integration complexity and cost.
Control Hierarchy
The fan-out of a module is where number of modules are directly controlled by a module, i.e. invoked by it. The fan-in of a module is where number of modules directly control a module, i.e. invoke it. In both cases the all modules subordinate to a module (Scope of Control) and they are all affected by a decision within a module (Scope of Effect). The aim is to keep scope of effect within scope of control, i.e. if a module is effected by the outcome of a decision then the module should be subordinate the module that made the decision.
Global vs Local Data
Module Complexity
There are many methods for measuring complexity, e.g. 'McCabe’s Cyclomatic Complexity'. McCabe’s cyclomatic complexity counts conditional statements and adds 1, if cyclomatic complexity is greater than ten then is considered too complex. However this method has been criticised as been be arbitrary, i.e. why ten, why not twenty, or five? Furthermore, there are other forms of complexity than conditional statements. Nevertheless, it can be useful in determining test cases:
Example
1. read workdatafile; grosswage = hours * rate 2. if (grosswage > 50000) then 3. tax calculation-1 else 4. tax calculation-2 5. netwage = grosswage - other deductions 6. if (netwage < 15000) then 7. extraallowances else 8. standardallowance 9. print wageslip
XXXXX -
Coupling
- Coupling is the interactions between modules. We want modules to as independent as possible and our goal is to reduce number of interactions. Some forms of interaction are less desirable than others, weak coupling is the goal. There are several levels of coupling:
- Altering another modules code:
- e.g. COBOL ALTER statement could change meaning of code
- Abnormal entry point:
- e.g. older languages used a GOTO statement which allowed you to direct a program to jump into the middle of a module
- Modifying data in another module:
- where possible a module should encapsulate its own data and should be defined as private/protected
- content coupling
- Shared/Global data:
- global/shared data should be avoided as it can be hard to track down which modules are changing data
- common coupling
- Procedure call parameter which is a switch:
- a value passed into a module which determines which path to execute – often a Boolean value
- i.e. a decision in one affects processing in other
- control coupling
- better to call different modules based on the decision
- Procedure call parameters which are pure data:
- nearly ideal, especially if number of parameters is small
- data coupling
- Passing a serial data stream:
- the weakest coupling (best), no transfer of control - data passed as if module writing to a file, receiving module as if reading from a file
- The weakest (best) coupling is therefore achieved by:
- procedure calls with a small number of pure data parameters, or
- passing a serial stream of data from one module to the other
-
Cohesion
- Cohesion defines interactions within a module. All statements within a module should contribute to the single function the method is designed to achieve. Some interactions are less desirable than others, strong cohesion is the goal. There are several levels of cohesion:
- Coincidental:
- statements in module are there by no great design
- difficult to state in words what the module does
- often because of some module size limit
- Logical:
- statements which carry out similar functions e.g. output, grouped together
- not one defined function and overly complex
- Temporal:
- statements which are executed at the same time
- e.g. initialise collections, open files
- functions unrelated
- better to have one initialisation control module which calls other specialised initialisation modules
- Communicational:
- functions acting on common data grouped together
- e.g. calculate and print total price
- carries out a number of different actions on one piece of data
- unnecessarily complex
- Sequential:
- operations collaborate to modify a piece of data
- Functional:
- single well-defined action on a single subject
- e.g. calculate average, obtain date
- strongest form of cohesion – most desirable
Coupling & Cohesion
Weak coupling & strong cohesion are good whereas, strong coupling & weak cohesion are bad. Strong cohesion tends to promote weak coupling and vice versa.
-
Java And Modularity
Java was not designed to have modules although this is being addressed in Java 9 (Sep 2016). Let’s consider the different Java mechanisms for grouping code (and data) e.g. methods, classes/objects, packages/libraries and JARs. First let’s consider the desirable characteristics of a module facility. Modules are autonomous unit of deployment which are separately compliable/deployable encouraging loose coupling. They also have a consistent and unique identities in the form of module IDs and version. However, as modules evolves a new version is created and older applications may need previous versions. Modules also provide us with documented requirements and dependencies such as standard compile-time, deployment facilities and meta-information. They promote open and understandable interfaces and allow us to hide implementation details (encapsulation).
Methods
A method is a named group of statements which has a defined interface through parameters and return statement, methods can declare local data. A well-defined method will have a single function and all statements within the method will contribute to that function. Therefore a method can minimize coupling and be cohesive. Methods are not separately deployable and there is no versioning so they are not really a module.
Classes
- Again a class (object) can have a well-defined interface which controls interactions with other classes (objects). A class defines its own data and methods should be aimed towards accessing and modifying that data. Therefore, good classes can minimize coupling and be highly cohesive. Where a class has a dependency on another class then access modifiers can be used to limit that connection, though this still increases coupling. Nested classes can be used to aid encapsulation. Classes also have several drawbacks:
- Object identity can be unreliable
- Interfaces are not versioned
- Classes not unique at deployment level
- Loose coupling best practice but not enforced
- Reuse can be difficult when third-party dependencies involved
However, compile-time tools attempt to address these issues.
Packages/Libraries
Packages are the closest mechanism Java has to a module. Packages are a set of classes and interfaces that are grouped together because of some shared purpose. Any Java library is in effect a module often with an API which defines its interface with the outside world. Applications often have one or more dependencies on external libraries, and libraries often have transitive dependencies on others.
Java Archive (JAR) (Oracle)
The java archive bundles multiple files into a single archive file. Class files and auxiliary resources are associated with applets and applications. The archive is consistently being updated and improves on many legacy deployment conventions. However the archive does not fulfil all requirements. Frameworks and platforms enhance JAR specification to provide support for modular systems. There are many benefits and some issues with JARs:
- Benefits:
- Security:
- can digitally sign the contents of a JAR file
- users who recognize signature can optionally grant software security privileges it wouldn't otherwise have
- Decreased download time:
- applet is bundled in a JAR file can be downloaded to a browser in a single HTTP transaction without the need for opening a new connection for each file
- Compression:
- JAR format allows you to compress your files for efficient storage
- Packaging for extensions:
- extensions framework provides means to add functionality to the Java core platform
- Package Sealing:
- optionally sealed so package can enforce version consistency
- Package Versioning:
- JAR file can hold data about the files it contains, such as vendor and version information
- Portability:
- mechanism for handling JAR files is a standard part of the Java platform's core API
- Issues:
- No intrinisic unqueness
- rarely used version number in a .jar manifest
- package names of classes & use in classpath only modularity support
- JAR Hell
- problems with class loading
- updated JAR can have same name as existing JAR
- runtime environment cannot distinguish and loads first one it finds
- app requires older version of library not in updated JAR file