Principles of Software Engineering

Published May 12, 2007
Advertisement
Here it is. A first round of Promit's Principles of Software Engineering. I may expand on this list in future posts, but this will get us started nicely.

1) All good software engineering is fundamentally motivated by KISS and YAGNI.
It's a fairly straightforward observation that the complexity of software increases as functionality and flexibility are increased. This manifests itself across the codebase. You can see it in the basic interfaces and APIs, in the user interface, and in the checkbooks as you spend millions of dollars maintaining a mess of super flexible goo of code. Good software strives for simplicity, and elegance, not infinite configurability. Don't try to design systems that solve every conceivable problem that could arise. If an unforeseen problem does arise, refactor to it, keeping things as simple as is reasonable.
2) A wide range of experience in multiple languages, paradigms, and environments is critical to a developer's ability to design software.
Every developer should know a large variety of languages. Lisp, Ocaml and/or Haskell, assembly, and C are on my list of important languages to know. Personally, I would not advocate developing in any of these languages (well, maybe Haskell), and I give my condolences to those of you who are forced to. Still, it's important to know these languages and many others because they give you a wider pool of knowledge and ideas to draw on. Many modern ideas in C++ are borrowed from Lisp, albeit in a rather contrived and roundabout manner. Software engineering isn't just about knowing the language you happen to be using and keeping a copy of Design Patterns on your desk. The more languages you learn, the more tools you have at hand, no matter what you're developing.

3) There are no legitimate uses of the singleton design pattern.
And I mean it. There is no correct way to use a singleton. There is no situation in which it makes sense. Not for logging, not for resource managers, and certainly not for your renderer or related objects. Everyone I've talked to has been a little bit hazy here, saying that maybe, in a particular scenario, a singleton could be a good solution. I am rejecting that outright. Singletons are a useless antipattern. If you have one, your design is broken.
If you want some actual discussion of these points, feel free to post a forum thread. I just wanted to list the points here; each one merits a discussion of its own.

[EDIT] Reordered the principles.
0 likes 4 comments

Comments

zeux
It actually finds me amusing (er... ahem... well, I hope you understood me :)), that most of the time when people criticize Singleton, they criticize not the pattern (which describes any object that has 2 properties - global access point, single instance), but some common implementation of it.

After that, when they're asked for reasoning, they say stuff like "You have problems with an order of initialization", "Late initialization can fragment memory and produce stalls", "It's not thread safe", "Why the fuck we keep writing Class::Instance()->Method(), it's long and obscure", etc. - while none of this relates to some implementations.

There are certainly drawbacks to the pattern itself, but they are not as overwhelming.

What are YOU talking about? Can you elaborate?
May 13, 2007 03:56 AM
David Neubelt
Quote:Original post by Promit
Here it is. A first round of Promit's Principles of Software Engineering. I may expand on this list in future posts, but this will get us started nicely.

1) There are no legitimate uses of the singleton design pattern.


I disagree. I believe that in most situations you may have encountered that the implementation of a singleton on a class has been a bad design. I do not feel that the pattern in itself has no legitimate use. I'm only talking about the unique instance of the pattern and not the other properties. I'd like to draw a comparison, give reasoning and finally provide an example.

First, I believe the pattern can be applied on objects as a modifier, much like the constant modifier. The implementation should not force the object to have all classes always have one instance but rather it should modify the class. For this program, my design says that having more then one instance of the class would be erroneous so let me mark it as having only one instance. Anything more would result in an error. It is a protection scheme much like statically typed languages. For another program, it would be acceptable for this class to have multiple instances so I would not mark it as a single instance.

In an ideal language it would look like this
class MyClass; // <-- declared
MyClass = single // <-- in an implementation file somewhere else

In C++ I've created a similiar construct that isn't as ideal.

CGlobal<MyClass> inst1;
CGlobal<MyClass> inst2; // second instance is the same as the first

Notice, that in another program I could decide to use a second instance by not using CGlobal and another class. This allows me to decide on a per project basis or even per library basis of whether it makes sense to mark the class as having one instance. The point I'm getting at is that in certain domains having more than one instance is erroneous and any constructs we can use to enforce the design will be a step in the correct direction and lead to less buggy code.

By allowing a choice of having multiple instances or single instance the design pattern creeps between the line of a singleton and multiton. Which gives it a more flexibility then it was originally designed.

It makes sense that any class that encapsulates physical interaction with devices on a system that you will want to have one copy of that class. Imagine that this class will be managing interactions with physical devices that only allow control of a volitle variable through one thread. Anymore attempts to access that variable will cause a system failure. Also, imagine that this certain instance of this class needs to be seen by one module.

By design:
* This module needs only one instance of the class any more will result in a hardware failure.
* The class needs global view from every other file in that module.

Solution:
* Protect the hardware by enforcing a singleton design on the class. It can manage state of the device and prevent any attempts to misuse it.
* Perhaps, on another console you can have multiple access to the device so do not enforce that the class always has to be a singleton. This should be a modifier for that particular class in that module.

-= Dave
May 13, 2007 07:18 PM
Emmanuel Deloget
I will write my remarks in two posts - one now, and one later. Feel free to react to this one before I post the next.

Quote:Original post by David Neubelt
Quote:Original post by Promit
Here it is. A first round of Promit's Principles of Software Engineering. I may expand on this list in future posts, but this will get us started nicely.

1) There are no legitimate uses of the singleton design pattern.


I disagree. I believe that in most situations you may have encountered that the implementation of a singleton on a class has been a bad design. I do not feel that the pattern in itself has no legitimate use. I'm only talking about the unique instance of the pattern and not the other properties. I'd like to draw a comparison, give reasoning and finally provide an example.

First, I believe the pattern can be applied on objects as a modifier, much like the constant modifier. The implementation should not force the object to have all classes always have one instance but rather it should modify the class. For this program, my design says that having more then one instance of the class would be erroneous so let me mark it as having only one instance. Anything more would result in an error. It is a protection scheme much like statically typed languages. For another program, it would be acceptable for this class to have multiple instances so I would not mark it as a single instance.

In an ideal language it would look like this
class MyClass; // <-- declared
MyClass = single // <-- in an implementation file somewhere else

In C++ I've created a similiar construct that isn't as ideal.

CGlobal<MyClass> inst1;
CGlobal<MyClass> inst2; // second instance is the same as the first

Notice, that in another program I could decide to use a second instance by not using CGlobal and another class. This allows me to decide on a per project basis or even per library basis of whether it makes sense to mark the class as having one instance. The point I'm getting at is that in certain domains having more than one instance is erroneous and any constructs we can use to enforce the design will be a step in the correct direction and lead to less buggy code.

By allowing a choice of having multiple instances or single instance the design pattern creeps between the line of a singleton and multiton. Which gives it a more flexibility then it was originally designed.

It makes sense that any class that encapsulates physical interaction with devices on a system that you will want to have one copy of that class.

I don't think so. See below.

Quote:Imagine that this class will be managing interactions with physical devices that only allow control of a volitle variable through one thread. Anymore attempts to access that variable will cause a system failure. Also, imagine that this certain instance of this class needs to be seen by one module.

By design:
* This module needs only one instance of the class any more will result in a hardware failure.
* The class needs global view from every other file in that module.

Solution:
* Protect the hardware by enforcing a singleton design on the class. It can manage state of the device and prevent any attempts to misuse it.
* Perhaps, on another console you can have multiple access to the device so do not enforce that the class always has to be a singleton. This should be a modifier for that particular class in that module.

-= Dave


Regarding the singleton pattern, I more and more think just like Promit - and be sure that each time I see a possible use for this pattern, I try to see if that can be a good idea. And (every time, so far), I give up because of the typicall problems of singletons. That's depressing, even if I don't like this pattern. My goal is then to find at least one reason to like it - one reason that would prove that the GoF featuring of this pattern was a right choice. Up to now, I must confess that I think that because of this pattern, the GoF book really hurted a whole generation of software designer.

First question: what does that mean, having only one instance of a class?

I don't even consider the classical argument that says that "you may need more than one instance in the future". That's YAGNI. But really, answer the question: what does it mean?

To me, it means that the possibility to create a class has been abused. I have two way to use it: locally or globally. Creating a class so that it's used locally (in a single function) only once in a whole system can be proven to be absurd, unless the goal is to simplify the code and the overall design of the application. But I think that one should try to refactor the code in a better way to avoid this situation. The other option is to use that single instance globally. And that leads us directly to my second question.

Second question: why should this single instance be global?

the only reason I see is that you need to access this instance from everywhere in the code - that's typically a good sign of a common design flow - a locality problem. I don't know anything which have to be global and used by all the services of an application. Or, to be more exact, I know only one thing: system resources. And global system resources are best used using functions, because the system is supposed to know about their state internally. If these functions need some id (representing an instance of something) to work, then this is not a global resource, since providing two different id will have a different result. So this cannot be represented by a singleton, as the id might change. If there is no intrinsic data to store, then why using an object? A function is going to do the job just as correctly.

And there is another thing about that: just like all global variable, the singleton shall be stateless or shall not be.

C++ is a bastard language in this respect. The most common system resources are implemented using global variables (not even singletons): cin, cout, cerr and clog. But this is just cosmetic code.

From a design point of view, the singleton really makes no sense, because stating that one particular class should be global but should not be created more than once makes no sense.
May 15, 2007 11:05 AM
David Neubelt
Quote:Original post by Emmanuel Deloget
Second question: why should this single instance be global?

the only reason I see is that you need to access this instance from everywhere in the code - that's typically a good sign of a common design flow - a locality problem. I don't know anything which have to be global and used by all the services of an application. Or, to be more exact, I know only one thing: system resources. And global system resources are best used using functions, because the system is supposed to know about their state internally. If these functions need some id (representing an instance of something) to work, then this is not a global resource, since providing two different id will have a different result. So this cannot be represented by a singleton, as the id might change. If there is no intrinsic data to store, then why using an object? A function is going to do the job just as correctly.


I'd like to quickly address your second question.
Quote:Second question: why should this single instance be global?

I am not going to argue that allowing a variable to be global across an application is bad design. I also believe it is bad design. I'm arguing that you can have global variables that are exposed to one module and only one module. In this sense, it is local to that module but global across all files in that module.

There are two points we can argue here a) the effectiveness of a global variable and b) the single instance of an object. I'm sure we can both agree the advantage of a global variable in a library. Let's focus on the single instance of an object.

Problem domain for single instances of objects

If you are writing a module that needs to handle a system resource then I disagree on your view that a function call will always be the best idea. Imagine that the system resource gives you ownership of volatile variables. Having multiple access to these variables can cause system crashes and to protect your design you limit the entry point to those variables. This type of hardware implies the code has to control the state of the hardware. In this type of system there can only be one variable that controls the hardware and you must model your design with this restriction to protect against system crashes.

You say that all systems should know their state internally by function calls but this just simply isn't true. I'm currently working on hardware that forces you to create a variable for the OS to use (they have it externed) and then make changes to that variable! There isn't always a fancy OS to hide all the details for you and for certain consoles there is a close one-to-one mapping of hardware state and variable state.

I suggest for this domain of problems:

* Wrap the volatile state of the OS in a class.
* Only allow a single instance (single entry point).
* Allow every file in the library access to that class.

-= Dave




May 15, 2007 11:37 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement