Features Screenshots ProjectSetup CodingStandards CodeAnalysis DesignStandards UnitTesting CodeSnippets

Design Standards

It is said that you cannot ‘test in’ quality, you can only build it in. A large part of building in quality lies in the architectural design of our software. A good design paves the way for rapid application development (due to reuse of components and simplicity of design), good developer testing (due to easily separable tests and the ability to attach to test data sources), and overall high quality of resulting products.

In general, a good design is simple, easy to understand and use, and loosely coupled. Our goal is to allow users to rapidly build games using a combination of reusable and custom components, based upon an common application framework (the engine).

3-Tier Architecture

The all Firefly engine components should follow the 3-tier architecture design.

The 3-Tier architecture has the following

Presentation Tier
This is the top-most level of the application. It presents data and information to the user, and comprises the windowing system, graphical display, and user interaction.

The Presentation Tier contains no business logic, but knows the appropriate business logic components to invoke.

The Presentation Tier contains no data access logic, but knows the appropriate data access components to invoke.

Business Logic Tier
Business Logic components know nothing of the presentation tier. They contain application-specific logic, primarily of two types:

Pure components are self-contained, and are not concerned with where their data comes from. This isolation is for two reasons:
  1. reusability (the component can be used for widely varying data sources),
  2. testability (simple object tests can be written for them).

Controller components are concerned with application state and transaction processing and may have knowledge of data access components.

Data Access Tier
Here information is stored and retrieved. This tier keeps data neutral and independent from application servers or business logic. Giving data its own tier also improves scalability and performance.

Benefits of a 3-tier architecture:

  1. Allows separate testing of each tier.
  2. Allows separate upgrade/modification of each tier without affecting the other tiers.

Loosely-coupled Design

Prefer data-driver designs
Data sources should be used for data access and system state. This is preferable to business logic components sharing data/state with each other, and allows clean decoupling of components.

Use abstract interfaces
  1. Abstract interfaces should be used where the designer foresees multiple implementations.
  2. Abstract interfaces pay big dividends with polymorphism.

Reduce component dependencies to a minimum
  1. A business logic component should not depend or supply info to the Presentation Tier.
  2. A “pure” business logic component should not connect to the Data Access Tier: “If your code base forces you to connect to the database just to check if two numbers have been added correctly, this should ring alarm bells.” -- Helmut Rubasch
  3. Don’t build more into a component than you need. Start sparse and add as needed.

Interfaces

Interfaces (and classes) should be abstractions of a single entity in the problem domain. It should be easy to describe what they represent in a single sentence.

Interfaces should be client-facing
Interfaces should reflect the clients’ view: They should provide information relevant to the client’s problem, and no more (Booch, 1994).

Interfaces should be small
Interfaces with a small number of properties and methods are easier to understand and to test. Always keep in mind: what is the minimum information required by the client?
Note: If you conclude that a large number of properties and methods are required, consider whether you’ve got the abstraction you are shooting for.

Prefer interfaces to inheritance
Don't use inheritance when the specialization that distinguishes a derived class from a base class is a feature that other classes will also need to support.

Generality and Reuse

Do not corrupt reusable components with specifics
Those components that are designed for reuse should be kept pure. Resist the temptation to make a quick fix that works for a particular application, but that ties application specifics into the component.

What should be made general?
Consider the following:
  1. Make components general if there is a foreseeable reuse.
  2. Resist the temptation to make everything general.
These two statements may seem contradictory, but both must be considered: However, making components reusable takes time – time which should be spent only if the designer foresees a good return on investment.

Miscellaneous

Composition vs. inheritance vs. interfaces
  1. Use inheritance when you have an is-a relationship.
  2. Use composition if you want to reuse a class, and the is-a relationship doesn’t apply
  3. Prefer interfaces if you want to confer polymorphism on a class.

Classes should be self-initializing
  1. The client should not have to be concerned with initialization issues, except for perhaps a call to an Initialize method.
  2. Prefer the class constructor to an Initialize() method.

Design for concurrency where needed
Pay attention to protecting those classes which may be reentrant. Document in the class header whether it is thread-safe.

Last edited Apr 17, 2009 at 5:26 PM by TheJeepinator, version 4

Comments

No comments yet.