TobiW's Design Study
(→Current Design) |
m (→Second Design) |
||
Line 54: | Line 54: | ||
After having specified the mistakes I did in my original design I came up with the following improvements: | After having specified the mistakes I did in my original design I came up with the following improvements: | ||
# Try to apply [[Tell, Don't Ask]] whenever I add methods or functionality to a class | # Try to apply [[Tell, Don't Ask]] whenever I add methods or functionality to a class | ||
+ | # '''Frame''' and '''Service''': Make all attributes and methods that are not accessed by other classes private | ||
# Split '''Frame''' class in | # Split '''Frame''' class in | ||
#* a class called '''Frame''' which represents only the main window | #* a class called '''Frame''' which represents only the main window | ||
Line 63: | Line 64: | ||
#* a class '''ServiceNetwork''' which encapsulates the network part (downloading from the internet) | #* a class '''ServiceNetwork''' which encapsulates the network part (downloading from the internet) | ||
#* a class '''ServiceParser''' which encapsulates the parsing of downloaded information ([[Strategy]]?) | #* a class '''ServiceParser''' which encapsulates the parsing of downloaded information ([[Strategy]]?) | ||
− | # | + | #* Use the [[Decorator]] and/or [[Strategy]] patterns to add or change functionality in service subclasses |
− | This | + | This leads to the following maxims/principles/patterns implemented (better or at all): |
* [[Separation of concerns]]: Much finer separation of the parts of '''Frame''' and '''Service''' than before | * [[Separation of concerns]]: Much finer separation of the parts of '''Frame''' and '''Service''' than before | ||
* Especially in the old fat '''Service''' class, implementing the [[Interface segregation principle]] made using it much easier | * Especially in the old fat '''Service''' class, implementing the [[Interface segregation principle]] made using it much easier | ||
+ | |||
+ | [[Image:CdFreemet2.png]] | ||
== Third Design (Final) == | == Third Design (Final) == | ||
Some of the class splitting might have been over the top so a more sensible and final design is developed here. | Some of the class splitting might have been over the top so a more sensible and final design is developed here. | ||
Sometime in 4th term ... | Sometime in 4th term ... |
Revision as of 02:02, 17 September 2009
Contents |
Overview
I decided to do the design study on a program I wrote back in Germany before I came here. I wrote it to have a single interface to the many meteorological sites I use to predict and learn something about the weather (mainly for my hobby flying sailplanes). It is quite useable but far from being finished. Technical details: it is written in Python using wxPython for the GUI, and has approx. 2500 lines of code (counted with CLOC).
The project is hosted on sourceforge: [1]
Here's the description I put up on Sourceforge: FreeMet is a meteorological program which provides weather maps, radar movies and forecasts in one GUI. New function can be added easily by writing new plugins which download, parse or process existing data available on the internet.
Design Study Goals
After having done a thorough design study I hope to
- have good documentation to make it easier for myself and others to start working on the program (again),
- discover major and minor design flaws,
- learn all those design patterns and code smells by looking at my code and trying to find them.
First I want to correct some major mistakes I did at my first design (like making too many attributes public). This first stage will only involve the most important classes (the main program class and the plugins). A third design will then incorporate improvements to other design flaws I made. Also, this will be the place to apply some design patterns that are not absolutely necessary but that could make the code more maintainable.
Python
Here, I want to list some Python-specific stuff:
- A list can store arbitrary values, a set automatically removes duplicates.
- A dictionary in Python is a Map/HashMap which maps string keys to arbitrary values.
- __init__() is the class's contructor and __del__() its destructor.
Current Design
- I included parts of wxPython to illustrate dependencies.
- The service class implements a plugin architecture
Classes
- FreeMetApp: Parses command line parameters and initialises the main frame.
- Frame: This is the central point of the application. It manages events from the main menu, the tree view (which is used to selected services), as well as from the timer which triggers the services to update themselves. Therefore, view, controller, and some data are all combined in this class.
- SplashScreen: Used by the main frame to indicate loading of a new set of services which can take up to a minute depending on the internet connection.
- Service: Base class/interface for all services. It is not completely abstract since common functionality for all services are implemented here, e.g. downloading from the internet and adding an image to the programs' GUI.
- Webcam: Example of a concrete service. It has to implement at least __init__, Download, and ShowNotebook. It uses a lot of functions that are provided by its super class.
OO Principles and Patterns
- Service: State design pattern. Depending on the active plugin downloading, parsing, and displaying information is different.
- Beware type switches and Avoid downcasting: I've learned from previous projects that controlling the logic with type switches is really bad and almost always leads to unmaintainable code. Therefore the plugin architecture for the services.
- Don't repeat yourself: there is no unnecessary duplicate code. For instance, functionality that is used by at least two services has been moved up to the Service base class.
Design Flaws
Even before doing a thorough analysis I've discovered several flaws in my design that will need some fixing:
- I usually write my programs around one central class (Frame) which handles most of the logic. This could be a large class smell and could conflict with avoid god classes as well as separation of concerns. Solution: Split large classes, Distribute system intelligence
- One reason why Frame is so big: Display and logic functionalities are mixed in one class (MVC).
- The Service class is also too big because it combines display (build notebook pages) and logic (downloading and parsing).
- After learning a lot about the Getter and setter policy, I'm sure I'll be able to eliminate many accessor methods.
- Related to getter/setter: some parts apply Tell, Don't Ask very well, others don't do at all and ask for information all the time.
- Probably because I was too lazy I didn't make all attributes private (Information hiding).
- Similar to the previous point, there are too many public methods that are only used internally.
- The Service base class implements a lot of functionality that is used by all concrete services: Avoid concrete base classes
Possible Improvements
- Apply Singleton pattern to Frame and SplashScreen (Singleton in Python).
Second Design
After having specified the mistakes I did in my original design I came up with the following improvements:
- Try to apply Tell, Don't Ask whenever I add methods or functionality to a class
- Frame and Service: Make all attributes and methods that are not accessed by other classes private
- Split Frame class in
- a class called Frame which represents only the main window
- a class that represents the central part of the main program (creating components, loading plugins)
- a class that handles the menu calls (including creating dialogs)
- a class that encapsulates the config management
- Split Service class in
- a class called Service which represents the main interface to a service (because of the other Service classes it might have to act as a Bridge or Adapter)
- a class ServiceNetwork which encapsulates the network part (downloading from the internet)
- a class ServiceParser which encapsulates the parsing of downloaded information (Strategy?)
- Use the Decorator and/or Strategy patterns to add or change functionality in service subclasses
This leads to the following maxims/principles/patterns implemented (better or at all):
- Separation of concerns: Much finer separation of the parts of Frame and Service than before
- Especially in the old fat Service class, implementing the Interface segregation principle made using it much easier
Third Design (Final)
Some of the class splitting might have been over the top so a more sensible and final design is developed here. Sometime in 4th term ...