I have been reading some old design books (SSADM, JSD.etc) on software engineering that deal mainly with a procedural view of creating systems. I was just wondering if we still use these old techniques e.g. splitting into subsystems and modularization even though programming languages at the moment is mainly object based? Thanks
Before designing, though, make sure you have a very good idea of the actual requirements of the software, what it will be used for, how it will be used, and who will use it. Many developers start thinking about design decisions before they fully understand the needs to be addressed by the product, and end up with a design that is ill-suited for the true purpose of the application.
I took an OOD course a few semesters back and learned a lot from it; like writing UML, and translating requirements documents into objects and classes. We learned sequence diagrams too but somehow I missed the lecture or something, they didn't really stick with me.
I've got a copy of Steve McConnell's Code Complete which I continually hear is amazing, here and elsewhere. I read the chapter on design and didn't seem to come out with the information I'm looking for. I know he says that it's not a cut and dried process, that it's mostly based on heuristics, but I can't seem to take all his information and apply it to my projects.
So what are things you do during the high level design phase (before you begin programming) to determine what are the classes you need (especially ones not based on any 'real world objects') and how will they interact with each other?
Create a class diagram. If you're a Java developer, NetBeans 6.7 from Sun has a UML module that allows for diagramming as well as round-trip engineering and it's FREE. Eclipse (an open source Java IDE), also has a modeling framework, but I have no experience with it. You may also want to try out ArgoUML, an open source tool.
As you come up with potential classes, don't think of them only in terms of what noun they represent, but what responsibilities they have. I've found this to be the biggest aid in figuring out how classes relate to each other during program execution. It's easy to come up with relationships like "a dog is an animal" or "a puppy has one mother." It's usually harder to figure out relationships describing run-time interactions between objects. You're program's algorithms are at least as important as your objects, and they're much easier to design if you've spelled out what each class's job is.
In the first iteration (or startup), I devise the general layout of the application according to the model objects, the algorithms, and the expected (really expected, not maybe expected) future directions. I don't write design documents, but if I have to coordinate multiple people, a rough sketch of the procedure is of course needed, together with an analysis of dependencies and guesstimate of the time needed. Try to keep this phase to a minimum if, like me, you prefer a more agile method. There are cases where a strong design phase is needed, in particular when everything is known and true about the logic of your program, and if you plan to have a lot of interactions between features in your code. In this case, use cases or user stories provide are a good high level idea, in particular for GUI apps. For command line apps, and in particular libraries, try to write "program stories" in which you code against the library you have to develop and check how it looks. These programs will become functional tests of your library when completed.
After this first iteration, you will have a better understanding on how things interact, got out the details and the rough spots, solved issues with a slapped duct tape patch. You are ready to make use of this experience to improve, clean, polish, divide what was too large, coalesce what was too fragmented, define and use design patterns, analyze performance bottlenecks and nontrivial security issues. In general, all these changes will have a huge impact on the unit tests you wrote, but not on the functional tests.
This is my general approach to software design. It is similar to spiral design, with short, three months iterations, and elements of Agile development, that allows you to learn the issues and get to know your software and its field of application. Of course, it's a matter of scalability, so if the application is so large to involve hundreds of developers, things are a bit more complex than this, but in the end I guess the idea is always the same, divide et impera.
The problem with large projects is that you can not oversee all the interactions between components. It is thus important to reduce the complexity of the project. Class and Sequence diagrams are too detailed for this phase of design.
Interpreter - Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language / Map a domain to a language, the language to a grammar, and the grammar to a hierarchical object-oriented design
Secondly - use OOP and classes only where a natural object hierarchy exists. Don't 'screw-in' it to that artificially. If no strict hierarchy exists (like in most business applications) - go for procedural/functional, or, at least use objects only as data containers with isolated accessors.
OOP should be viewed as one of the paradigms, not as the superior paradigm. OOP is good for solving certain kind of problems, like developing a GUI library. It also fits into the style of software development usually followed by large software companies - an elite team of designers or architects lays down the software design in UML diagrams or some other similar medium and a less enlightened team of developers translate that design into source code. OOP offer little benefit if you are working alone or with a small team of highly talented programmers. Then, it is better to use a language that supports multiple paradigms and will help you to come up with a prototype fast. Python, Ruby, Lisp/Scheme etc are good choices. The prototype is your design. Then you improve on that. Use the paradigm that is best to solve the problem at hand. If needed, optimize hot spots with extensions written in C or some other systems language. By using one of these languages, you also get extensibility for free, not just at the programmer level but also at the user level. Languages like Lisp can dynamically generate and execute code, which means your users can extend the application by writing small code snippets, in the language that the software itself is coded! Or if you choose to write the program in C or C++, consider embedding an interpreter for a small language like Lua. Expose functionalities as plugins written in that language.
Derived from understanding system-level requirements, identifying domain entities and their relationships provides a basis for understanding and designing systems for maintainability, testability, and incremental development. Because there is often a gap between understanding the problem domain and interpreting requirements, domain modeling is essential to Agile development at scale. Driven partly by object-oriented design approaches, domain modeling envisions the solution as domain objects collaborating to fulfill system-level scenarios.
Domain modeling is an essential tool for software engineering: if you only model one thing in Agile, model the domain. Even a relatively small domain modeling effort is an excellent tool for reducing the complexity of system development. It helps clarify requirements and the design intent. It reflects the current understanding of entities and their relationships and responsibilities with the problem domain. Figure 1 shows an example of a domain model for a consumer subscription management system.
Domain modeling is helpful for analysis and often a good conceptual model for system design. Domain modeling is one of the key design patterns/approaches that assume deriving the solution object model directly from the problem domain while preserving both behavior and data (see [3]). See [2] for a systematic and detailed outline of such best practices, known by Domain-Driven Design. This approach provides a natural and very effective way of managing the inherent complexity of software development that is vital at scale. Figure 5, adapted from [3], chapter 2, compares the effort spent on enhancing software functionality versus complexity when the design is based on the domain, data structure, or transaction scripts.
This is just one example of how domain modeling can be effectively used for Commonality-Variability Analysis (CVA) to foster effective system object models. (See [5], chapter 8 for more detail on the CVA method).
Nonfunctional Requirements, on the other hand, represent the primary reason for building system design around data structure or transaction scripts rather than the domain model. Typically NFRs like performance or scalability may result in domain logic being spread across large SQL scripts, where too much logic is in the client-side validation scripts, and so on. Even though the use of such a transaction script approach (see [3], chapter 9) can be legitimate in some instances, it should be used as an exception rather than the rule. Few properly implemented exceptions will allow the Agile enterprise to benefit from a domain-oriented approach.
Domain models provide a vital link between the problem domain and the code. Domain-oriented design approaches enable controlling the growing complexity and cost of maintenance and enhancement effort. Domain modeling is a highly visual and collaborative effort that creates a shared understanding of the priorities and better ways to implement them. It typically involves System Architects, Product Management, Agile Teams, and stakeholders. No other models cover as many aspects of Agile development at scale. If you only model one thing, model the domain.
aa06259810