Neste último artigo sobre SCJD, quero finalizar apresentando meu arquivo de decisões arquiteturais, com o qual fui aprovado quando prestei a prova. Vale a pena lembrar que as opções abaixo foram por mim decididas mediante os requisitos e as circunstâncias do meu projeto, não sendo aplicáveis para qualquer outro. O meu objetivo aqui é mostrar para os candidatos interessados um exemplo modelo para que seja usado como “template” – ponto de partida. Outra coisa importante é que as decisões documentadas podem de alguma forma ajudar ou oferecer um “norte” para que os candidatos possam fazer as suas em seus projetos. Volto a dizer que essa foi uma das provas para a qual eu mais tive que estudar, mas que também foi na que eu mais aprendi.
Continuo à disposição para qualquer eventual ajuda.
Design
Architecture
I decided to use 3-layer (GUI->BUSSINES->PESISTENCE->FILE.db) instead of 2-Layer (GUI->PERSISTENCE->FILE.db).
I decided to create a bussines layer between gui and persistence layer because:Loosely coupled Avoid coupling of the details of persistence in the gui layer.
- Centralization and encapsulation of business rules.
- Code reuse facilitates the addition of different types of client such as a web application.
- Independence between layers.
- Maintainable and extensible.
- Transparency in data access.
- Deploy between different host.
Implementation
- The suncertify.db package implements persistence layer.
- The suncertify.bussines package defines polymorphically bussines layer.
- The suncertify.local implements local bussines layer.
- The suncertify.remote implements remote bussines layer.
- The suncertify.view implements view layer.
Designer Patterns
The following patterns were used:
- suncertify.db.Data implemented Singleton.
- suncertify.remote.RemoteRoomServiceImp implemented Adapter.
- suncertify.bussines.Room implemented Transfer Object.
- suncertify.remote.ServiceFactoryImp implemented Factory.
- suncertify.view.ConnectionWindow implemented Template Method.
Exception Handling
I decided to create and use object exceptions approach instead of return error codes because:
- Robust code.
- Separate the error handling from the application logic.
- Exceptional conditions can not be ignored because there must always be a catch handler.
- OOP approach Treated as an object encapsulating all the necessary information.
- Decreases the complexity.
I created an exception for each layer that was used to isolate the occurrences of exceptional conditions:
- *Persistence Layer: suncertify.db.DataBaseException.
- *Bussines Layer: suncertify.bussines.BussinesException.
- *View Layer: suncertify.view.ViewException.
Server
Network RMI vs. Socket
I decided to use RMI because:
- Transparency and usability – communication mechanism is completely hidden by technology.
- Object based semantic – remote objects look and feel just like local objects.
- Protocol – there is no need to worry about designing protocol between client and server.
- I did not want implement any mechanisms for remote communication.
Database File Selection
I chose not to let the user choose or change the database file when connected remotely because:
- The assignment did not mention any requirement on the subject.
- Avoid increase the complexity of the code unnecessarily.
Shutdown Hook
I decided to implement shutdown hook with Runtime.getRuntime().addShutdownHook() because:
- Close the database file properly when the application is closed.
Persistence Layer
File Acess
I decided to implement database handler with just 2 classes:
- suncertify.db.Data implements mandatory interface and manages access to the file.
- suncertify.db.RecordManager utility class that encapsulates all details about the database schema.
I decided to use RandowAcessFile instead of separate DataInputStream and DataOuputStream classes because alow (it alows?) randow acess to file.
Hard-Code Database Schema
I decided to use hard-code database schema instead of dynamically load because:
- The assignment did not mention any requirement on the subject.
- The assignemnt did not mention the possibility of having other types of database files.
- Avoid increase the complexity of the code unnecessarily.
No Cache
I decided to always read the records directly from the database file instead of using cache approach because:
- The assignment did not mention any performance requirement.
- Easy understanding for junior programmers.
- Avoid increase the complexity of the code unnecessarily.
Database Exception
I decided to throw a RuntimeException (suncertify.db.DataBaseException) instead of encapsulate the errors occurred in the exceptions defined by the mandatory interface because:
- There were errors that made no sense to throw the exception defined by the mandatory interface.
- It was not possible to add new exceptions in the mandatory interface.
Magic Cookie
I decided to validate the magic cookie preventing the user to enter an invalid database file.
The validation implemented was hard-code instead of dynamically because:
- The assignment did not mention any requirement on the subject.
- The assignemnt did not mention the possibility of having other types of database files.
- Avoid increase the complexity of the code unnecessarily.
Primary Key
I decided to use the file position as primary key because:
- The assignment did not mention any requirement on the subject.
I decided not to reuse the deleted primary key because:
- The assignment did not say it was mandatory.
- I decided not to make delete operation for the application.
- Avoid increase the complexity of the code unnecessarily.
Never Throw DuplicateKeyException
DuplicateKeyException defined in the createRecord(String[] data) method (mandatory interface) will not be thrown resulting from the choice not to reuse the deleted primary keys.
Create Record, Update Record and Delete Record Methods
- I decided not to implement any validation in the fields.
- I decided to fill white space fields with size less than that defined in the database schema.
- I decided to ignore the character of the fields that far exceeded the maximum size defined in the database schema.
Reasons:
- The assignment did not mention any requirement on the subject.
- I decided not to make these operations for the application.
- I decided to implement blocking mechanisms properly as required in the assignment.
- I only supply basic implementation for these methods due to mandatory interface.
Find By Criteria Method
I decided to use 2 ‘for’ statement and String.startsWith() method instead of regular expression because:
- Easy implementation.
- Easy understanding for junior programmers.
- Avoid increase the complexity of the code.
Lock
I decided to use synchronized methods, Object.notifyAll() and singleton patter approach instead of java.util.concurrent API because:
- The assignment did not mention any performance requirement.
- Easy implementation.
- Easy understanding for junior programmers.
- Avoid increase the complexity of the code.
Lock Owner
I used the cookie approach to identify the lock owner because:
- It was specified in the mandatory interface.
Lock Cookie
I decided to use System.nanoTime() to generate lock cookie because:
- Simple and easy.
- Secure enough to distinguish one client from the others.
Error Message
I decided to separate all the error messages in a specific class (suncertify.db.PersitenceMessages) because:
- Facilitate maintenance.
- Facilitate future translations.
- I18N simple mechanism that does not increase the complexity of the code.
Singleton Patter
I decided to use the singleton pattern in database management class because:
- Ensure the creation of only one instance.
- Properly implement the lock control.
Businnes Layer
Bussines Exceptions
I decided to create a base exception class that represents all the errors occurred within the layer instead of creating several different classes because:
- Isolate and hide details of the errors internally occurred.
- Define a communication error protocol based on layer.
- Avoid creating many exceptions classes.
Error Message
I decided to separate all the error messages in a specific class (suncertify.local.BussinesMessages) because:
- Facilitate maintenance.
- Facilitate future translations.
- I18N simple mechanism that does not increase the complexity of the code.
Tranfer Object Patter
I decided to use the transfer object patter creating a class that wraps the fields of a room instead of using array of String in the signature of the methods.
Reasons:
- Make a code easier to read.
- Easy understanding for junior programmers.
- Improve performance when used in remote mode.
Bussines Methods
I decided not to provide methods to insert, update, delete and unbook because:
- The assignment did not say it was mandatory.
- Avoid unnecessary increase in code.
I decided to provide the following methods:
- getRooms get all rooms.
- search search room using criteria.
- book book a room.
I decided to make two separate search methods instead of one because:
- Make code easier to read.
- Easy understanding for junior programmers.
- OOP principles override over logic.
I decided to implement the AND criteria logic within the business layer instead of the view layer because:
- Avoid coupling of the details of criteria logic in the gui layer.
- Reuse in different types client such as a web application.
I decided to implement the OR and AND criteria logic with just one method, using an criteria object instead of two separated methods because:
- In this case, it made no sense, in my opinion, to separate and use overload because I see the search operation with OR/AND as being an unique operation.
Book Method
I decided to implement a validation that checks the availability of the room avoiding:
- Book override.
- Data inconsistencies.
- This is a business rule and should stay within the business layer.
48 Hours Rule
I decided not to implement anything about the “48 Hours Rule” because:
- The assignment did not say it was mandatory.
- Avoid increase the complexity of the code unnecessarily.
Adapter Patter
I decided to use the adapter pattern in remote service implementation because:
- Create a level of separation from the networking approach and application logic.
- Decouple implementation details of the RMI technology from the of application logic.
- Code reuse.
Factory Patter
I decided to use the factory pattern in remote service implementation because:
- Ensure that each remote client connected to the RMI server does not share the same business object.
- Proper identifier mechanism for lock owner.
- Reduce the number of remote objects that need be registered.
View Layer
GUI Texts and Error Messages
I decided to separate all the error messages in a specific class (suncertify.view.ViewMessages).
I decided to separate all the GUI texts titles, labels, mnemonics and tool tips in a specific class (suncertify.view.ViewTexts).
Reasons:
- Facilitate maintenance.
- Facilitate future translations.
- I18N simple mechanism that does not increase the complexity of the code.
Template Method Patter
I decided to use the template method pattern in a GUI super-class because:
- Define general skeleton of an algorithm in an operation, deferring some steps to be defined in sub-classes.
- Code reuse.
No MVC Patter
I decided not to use the MVC pattern in GUI constructions because:
- The assignment did not mention any requirement about display data throught multiples sources.
- In my opinion, it is an overhead to use the MVC and create all the necessary classes in so few GUI operations.
- Avoid increase the complexity of the code.
- The bussines layer encapsulates rules that can be reused in different types of clients, such as a web application.
No Background Threads
I decided not to use background threads to deal with the user events, leaving everything to be queued on the event dispatch thread because:
- The assignment did not mention any requirement on the subject.
- In my opinion, there is no operation currently available that needs to be executed in another thread.
- In my opinion, with current available operations, the user can not do anything in the system while performing some action. This may change depending on new business operations added.
- Avoid increase the complexity of the code unnecessarily.
Look And Feel
I decided only used default java 6 look and feel because:
- The assignment did not mention any requirement on the subjects.
- Avoid unnecessary increase in code.
Layout Managers
I decided to use the following layout managers:
- Default BorderLayout.
- FlowLayout.
- GridBagLayout.
Reasons:
- The assignment did not mention any requirement on the subjects.
- They were the ones I felt that fit better in the construction of graphical user interfaces.
Input Mask
I decided not to use any input mask, leaving the user type everything because:
- The assignment did not say it was mandatory.
- I decided to centralize the implementations of all fields validations in their relevant layer.
- Code reuse.
List Room GUI Startup
I decided not to load all the rooms in the presentation of the main GUI because:
- The assignment did not mention any requirement on the subject.
- Leave that decision to the user to load all the rooms or filter using some criteria.
Others GUI Functions
I decided not to provide insert, update, delete and unbook GUI functions because:
- The assignment did not say it was mandatory.
- I decided not to implement these operations in the business layer.
- Avoid unnecessary increase in code.
General
I decided not to implement anything that would solve the following situations:
- Lost locks.
- Dead lock.
- General lock on the database during the shutdown.
- Client crashes.
- Sorting columns.
- Paging data.
- Notify view layer about nonrepeatable read.
Reasons:
- The assignment did not mention any requirement on the subjects.
- Avoid increase the complexity of the code.
No Log
I decided not to use log because:
- The assignment did not say it was mandatory.
- Avoid unnecessary increase in code.
Design
No Code Comments
I decided not to write any code comments because:
- In my opinion, there was no awkward or complex code that needed it.