Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I've had a colleague that told me he once worked for a company that had as a policy to never have conditionals ("if" and "switch" statements) in the code and that they let all the decisions in the code be done using polymorphism and (I'm guessing) some other OO principles.
I sort of understand the reasoning behind this, of having code that is more DRY and easier to update, but I'm looking for a more in-depth explanation of this concept. Or maybe it's part of a more general design approach.
If anyone has any resources for this or would be willing to explain or even have some more terms related to this I can use to find more answers I'd be much obliged.
I found one question on SO that was kind of related but I'm unfamiliar with C++ so I don't understand too much of the answers there.
(I'm no OO guru btw but I can manage)
I'm most proficient in PHP, and after that Python so I'd prefer info that uses those languages.
Update: I'll ask my colleague for more info on what he meant exactly.
Update 2015: after some more years of experience in programming I see now that the aim of this policy was probably to prevent programmers from adding functionality in a haphazard way by just adding conditionals (if statements) in certain places. A better way to extend software is to use the "Open/Closed principle" where software is extended by using inheritance and polymorphism. I strongly doubt whether the policy was super strict on all conditionals as it's kinda hard to go completely without them.
There are some resources on the Anti-IF Campaign site, such as this article.
I believe it's a matter of degree. Conditionals aren't always bad, but they can be (and frequently are) abused.
Additional thoughts (one day later)
Refactoring: Improving the Design of Existing Code is a good reference on this subject (and many others). It covers Replace Conditional with Polymorphism. There's also a new one, Replace Conditional with Visitor, on the web site.
I value simplicity and single responsibility over removing all if statements. These three goals often coincide. Static analysis tools that support the cyclomatic complexity metric can quickly point out code with nested or serial conditionals. The if statements may remain post-refactoring, but could be broken out into smaller methods and/or multiple classes.
Update: Michael Feathers wrote an article on Unconditional Programming.
This is a popular topic: Phil Haack on Death to the IF statement!
After some years of programming I return to my own question, the context of which I now understand a little better.
There's a good talk by Sandi Metz where she refactors a really hairy ball of if-statements to something waaay less hairy: https://www.youtube.com/watch?v=8bZh5LMaSmE
I read the post you linked and it seems that they were mostly talking about removing the need for conditionals within a class, not to be confused with all code in general. The idea being that if you need to check an object's state (using a conditional) to determine if it has certain functionality, then in actuality you have two objects (one that supports the functionality and one that does not) and should define them as two related classes instead.
I've had a colleague that told me he once worked for a company that
had as a policy to never have conditionals ("if" and "switch"
statements) in the code and that they let all the decisions in the
code be done using polymorphism and (I'm guessing) some other OO
principles.
I think your colleague misunderstood something or used the wrong words to explain it.
And you can't completely avoid conditional statements.
There is a thing to say: proliferating of if statements inside OOP may be a symptom of bad programming. Some example:
Don't use if to check return value of the function like a old C-style programming:
int ret = some_func();
if (ret != null)
//do something
This was typical in C code but with OOP you should use exception:
try{
do_something();
}catch(Exception e){
e.printStackTrace(); //why I was not able to do something
handle(e); //there is something else I could do to handle the occurred error
}
Sometimes if statements proliferation is related to a bad design. Consider the follow example in Java:
BaseClass base;
if (base instanceof DerivedClassOneFromBase){
DerivedClassOneFromBase d = (DerivedClassOneFromBase)base;
d.methodOne();
}else if (base instanceof DerivedClassOneFromBase){
DerivedClassTwoFromBase d = (DerivedClassTwoFromBase)base;
d.methodTwo();
}
This is another example of bad if statements, probably related to a bad design. If the two derived object would have a common method defined in their base class BaseClass, the you could have called that method instead of checking their concrete type and casting them:
base.commonMethod();
Sometimes conditionals inside methods are bad as they are a sign that you are just performing multiple functions or multiple types methods in the one method.
If you have a class called Automobile and subclasses such as Car and Bike, and a method such as:
drive(Automobile a)
if (a.isCar)
// do stuff
else if (a.isBike)
// do stuff
you are most likey doing something wrong. Even if it is not a switch based on type, it can often be wrong. If the method is performing multiple functions depending on some variable, it is often trying to do more than one thing and probably should be separated into multiple methods.
For example:
save(Car c)
if (c is new)
// do some stuff
else if (c is old)
// do some stuff
could potentially be broken up into save and update, as they are two different functions. Though it does depend.
Completely banning if statements would be silly though as they have many valid use cases.
Avoiding conditionals does not necessarily mean you need to get it done by polymorphism or inheritance, take for example :
You have 3 different folders to store uploaded images, uploaded videos and uploaded pdf
You can write code as :
uploadMedia(mediaType){
if(mediaType == images){
uploadTo("myProject/images");
}else if(mediaType == videos){
upoloadTo("myProject/videos);
}else if(mediaType == pdf){
uploadTo("myProject/pdf");
}
}
Another alternative that people might use is switch-case :
uploadMedia(mediaType){
switch(mediaType){
case : images
uploadTo("myProject/images");
break;
case : videos
uploadTo("myProject/videos");
break;
case : pdf
uploadTo("myProject/pdf");
break;
}
}
But then you can totally avoid conditional statement by using something like dictionary/hashmap/json (Depending upon whatever you work with) :
For example :
HashMap<String,String> mediaMap = new HashMap<>();
mediaMap.put("images","myProject/images");
mediaMap.put("videos","myProject/videos");
mediaMap.put("pdf","myProject/pdf");
//mediaType can be images/videos/pdf same as keys of mediaMap
uploadMedia(mediaType){
uploadTo(mediaMap.get(mediaType));
}
This is sort of pseudo code so there might be syntax mistakes, but overall this concept is also helpful in avoiding conditionals. Also line of code can be reduced.
Related
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
I recently learned that switch statements are bad in OOP, perticularly from "Clean Code"(p37-39) by Robert Martin.
But consider this scene: I'm writing a game server, receiving messages from clients, which contain an integer that indicates player's action, such as move, attack, pick item... etc, there will be more than 30 different actions. When I'm writing code to handle these messages, no metter what solutions I think about, it will have to use switch somewhere. What pattern should I use if not switch statement?
A switch is like any other control structure. There are places where it's the best/cleanest solution, and many more places where it's completely inappropriate. It's just abused way more than other control structures.
In OO design, it's generally considered preferable in a situation like yours to use different message types/classes that inherit from a common message class, then use overloaded methods to "automatically" differentiate between the different types.
In a case like yours, you could use an enumeration that maps to your action codes, then attach an attribute to each enumerated value that will let you use generics or type-building to build different Action sub-class objects so that the overloading method will work.
But that's a real pain.
Evaluate whether there's a design option such as the enumeration that is feasible in your solution. If not, just use the switch.
'Bad' switch statements are often those switching on object type (or something that could be an object type in another design). In other words hardcoding something that might be better handled by polymorphism. Other kinds of switch statements might well be OK
You will need a switch statement, but only one. When you receive the message, call a Factory object to return an object of the appropriate Message subclass (Move, Attack, etc), then call a message->doit() method to do the work.
That means if you add more message types, only the factory object has to change.
The Strategy pattern comes to mind.
The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.
In this case, the "family of algorithms" are your different actions.
As for switch statements - in "Clean Code", Robert Martin says that he tries to limit himself to one switch statement per type. Not eliminate them altogether.
The reason is that switch statements do not adhere to OCP.
I'd put the messages in an array and then match the item to the solution key to display the message.
From the design patterns perspective you can use the Command Pattern for your given scenario. (See http://en.wikipedia.org/wiki/Command_pattern).
If you find yourself repeatedly using switch statements in the OOP paradigm, this is an indication that your classes may not be well design. Suppose you have a proper design of super and sub classes and a fair amount of Polymorphism. The logic behind switch statements should be handled by the sub classes.
For more information on how you are removed these switch statements and introduce the proper sub-classes, I recommend you read the first chapter of Refactoring by Martin Fowler. Or you can find similiar slides here http://www1.informatik.uni-wuerzburg.de/database/courses/pi2_ss03_dir/RefactoringExampleSlides.pdf. (Slide 44)
IMO switch statements are not bad, but should be avoided if possible. One solution would be to use a Map where the keys are the commands, and the values Command objects with an execute() method. Or a List if your commands are numeric and have no gaps.
However, usually, you would use switch statements when implementing design patterns; one example would be to use a Chain of responsibility pattern to handle the commands given any command "id" or "value". (The Strategy pattern was also mentionned.) However, in your case, you might also look into the Command pattern.
Basically, in OOP, you'll try to use other solutions than relying on switch blocks, which use a procedural programming paradigm. However, when and how to use either is somewhat your decision. I personally often use switch blocks when using the Factory pattern etc.
A definition of code organisation is :
a package is a group of classes with coherant API (ex: Collection API in many frameworks)
a class is a set of coherent functionalities (ex: a Math class...
a method is a functionality; it should do one thing and one thing only. (ex: adding an item in a list may require to enlarge that said list, in which case the add method will rely on other methods to do that and will not perform that operation itself, because it's not it's contract.)
Therefore, if your switch statement perform different kinds of operations, you are "violating" that definition; whereas using a design pattern does not as each operation is defined in it's own class (it's own set of functionalities).
I don't buy it. These OOP zealots seem to have machines that have infinite RAM and amazing performance. Obviously with inifinite RAM you don't have to worry about RAM fragmentation and the performance impacts that has when you continuously create and destroy small helper classes. To paraphrase a quote for the 'Beautiful Code' book - "Every problem in Computer Science can be solved with another level of abstraction"
Use a switch if you need it. Compilers are pretty good at generating code for them.
Use commands. Wrap the action in an object and let polymorphism do the switch for you. In C++ (shared_ptr is simply a pointer, or a reference in Java terms. It allows for dynamic dispatch):
void GameServer::perform_action(shared_ptr<Action> op) {
op->execute();
}
Clients pick an action to perform, and once they do they send that action itself to the server so the server doesn't need to do any parsing:
void BlueClient::play() {
shared_ptr<Action> a;
if( should_move() ) a = new Move(this, NORTHWEST);
else if( should_attack() ) a = new Attack(this, EAST);
else a = Wait(this);
server.perform_action(a);
}
Context: I need to explain "composed methods" to a group of mixed-experience.
I think I heard about it first while reading Beck's Smalltalk Best practices. I've personally not had too many issues writing such methods - however in the local code-wilderness, I've seen quite a few instances where the lack of composed methods had created indecipherable Blobs... and I was in the minority. So I'm taking them through CleanCode - where this one popped up again.
The premise is quite simple.
"Functions should be short, do one
thing well and have an
intention-revealing name. Each step in
the body of the method should be at
the same level of abstraction."
What I'm struggling with a check for the "same level of abstraction".. viz.. forgive the pun a bit abstract for beginners.
My current explanation would be similar to SICP's "wishful thinking". (Imagine the ideal set of steps and then worry about implementation/making it happen.").
Does anyone have a better set of rules / an acid test to evaluate your decisions while writing composed methods ?
Same level of abstraction - examples:
void DailyChores()
{
Dust();
Hoover();
MopKitchenFloor();
AddDirtyClothesToWashingMachine();
PlaceDetergentInWashingMachine();
CloseWashingMachineDoor();
StartWashingMachine();
Relax();
}
Hopefully it should be clear that the WashingMachine saga would be better extracted into a separate method entited WashDirtyLaundry();
Arguably the MopKitchenFloor should also be in a separate method entitled CleanKitchen() as quite likely you would want to extend this in the future to include WashPots(), DefrostFridge() etc.
So a better way would be to write as follows:
void DailyChores()
{
Dust();
Hoover();
CleanKitchen(CleaningLevel.Daily);
WashDirtyClothes();
Relax();
}
void WashDirtyClothes()
{
AddDirtyClothesToWashingMachine();
PlaceDetergentInWashingMachine();
CloseWashingMachineDoor();
StartWashingMachine();
}
void CleanKitchen(CleaningLevel level)
{
MopKitchenFloor();
WashPots();
if(level == CleaningLevel.Monthly)
{
DefrostFridge();
}
}
enum CleaningLevel
{
Daily,
Weekly,
Monthly
}
In terms of "rules" to apply to code not following this principle:
1) Can you describe what the method does in a single sentence without any conjunctions (e.g. "and")? If not split it until you can. e.g. In the example I have AddDirtyClothesToWashingMachine() and PlaceDetergentInWashingMachine() as separate methods - this is correct - to have the code for these 2 separate tasks inside one method would be wrong - however see rule 2.
2) Can you group calls to similar methods together into a higher level method which can be described in a single sentence. In the example, all of the methods relating to washing clothes are grouped into the single method WashDirtyClothes(). Or in consideration of rule 1, the methods AddDirtyClothesToWashingMachine() and PlaceDetergentInWashingMachine() could be called from a single method AddStuffToWashingMachine():
void AddStuffToWashingMachine()
{
AddDirthClothesToWashingMachine();
PlaceDetergentInWashingMachine();
}
3) Do you have any loops with more than a simple statement inside the loop? Any looping behaviour should be a separate method. Ditto for switch statements, or if, then else statements.
Hope this helps
I take "acid test" to mean you would like some concrete rules that help embody the abstract concept in question. As in "You might have mixed levels of abstraction if..."
You might have mixed levels of abstraction if...
you have a variable in the function that is only used for part of the function.
you have multiple loops in the function.
you have multiple if statements who's conditions are independent of each other.
I hope others will add to the above...
EDIT: Warning - long answer from self-taught guy ahead . . .
In my very humble opinion (I am TOTALLY learning here as well) it seems BonyT and Daniel T. are on the right track. The piece which might be missing here is the design part. While it is safe to say that refactoring/composing will always be a necessity, might it also be as safe to say that (ESPECIALLY with beginners!) proper design up front would be the first, second, and third steps to properly composed code?
While I get what you are asking (a test/set of tests for code composition), I think BonyT is applying the earliest of those tests during the "pseudocode" part of the design process, no?
Obviously, in the early stages of project planning, strong design and experienced coders will sniff out the obvious places ripe for composition. As the work progresses, and the team is beginning to fill these initial code stubs with body, some slightly more obtuse exaples are bound to crop up. BonyT's example presents these first two steps quite well.
I think there comes a point at which experience and a finely-tuned "nose" for code smell comes in - in other words, the part you may not be able to teach directly. However, that is where Daniel T's answer comes in - while one may not be able to develop concrete ACID-type "tests" for proper composition, one CAN employ techniques such as Daniel proposes to seek out potential smelly code. Detect "hints" if you will, that should at least prompt further investigation.
If one is not certain whether things are composed at the proper level, it might make sense to work through a particular function and attempt to describe, step-by-step, what is going on in simple, single sentences without conjuctions. This is probably the most basic ACID-type test that could be performed. Not to mention, this process would by default end up correctly documenting the code . . .
In response to BonyT you imply that his pseudocode/method stubs make the next step obvious - I am betting that if one walks through a function and describes things step by step, one will often find that indeed, the next step either obviously follows at the same level of detail, or belongs elsewhere. While there will obviously be cases (many, with complex code) where things are not so neat, I propose that this is where nothing but experience (and possibly genetics!) come in - again, things you can't teach directly. At that point, one must examine the problem domain, and determine a solution which best fits the domain (and also be prepared to change it down the road . . .). Once again, properly documenting the "in-between" cases with short, simple statements (and narrative describing decisions in gray areas) will help the poor maintenence guy down the road.
I realize that I have proposed nothing new here, but what I had to say was longer than a comment would allow!
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
I recently learned that switch statements are bad in OOP, perticularly from "Clean Code"(p37-39) by Robert Martin.
But consider this scene: I'm writing a game server, receiving messages from clients, which contain an integer that indicates player's action, such as move, attack, pick item... etc, there will be more than 30 different actions. When I'm writing code to handle these messages, no metter what solutions I think about, it will have to use switch somewhere. What pattern should I use if not switch statement?
A switch is like any other control structure. There are places where it's the best/cleanest solution, and many more places where it's completely inappropriate. It's just abused way more than other control structures.
In OO design, it's generally considered preferable in a situation like yours to use different message types/classes that inherit from a common message class, then use overloaded methods to "automatically" differentiate between the different types.
In a case like yours, you could use an enumeration that maps to your action codes, then attach an attribute to each enumerated value that will let you use generics or type-building to build different Action sub-class objects so that the overloading method will work.
But that's a real pain.
Evaluate whether there's a design option such as the enumeration that is feasible in your solution. If not, just use the switch.
'Bad' switch statements are often those switching on object type (or something that could be an object type in another design). In other words hardcoding something that might be better handled by polymorphism. Other kinds of switch statements might well be OK
You will need a switch statement, but only one. When you receive the message, call a Factory object to return an object of the appropriate Message subclass (Move, Attack, etc), then call a message->doit() method to do the work.
That means if you add more message types, only the factory object has to change.
The Strategy pattern comes to mind.
The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.
In this case, the "family of algorithms" are your different actions.
As for switch statements - in "Clean Code", Robert Martin says that he tries to limit himself to one switch statement per type. Not eliminate them altogether.
The reason is that switch statements do not adhere to OCP.
I'd put the messages in an array and then match the item to the solution key to display the message.
From the design patterns perspective you can use the Command Pattern for your given scenario. (See http://en.wikipedia.org/wiki/Command_pattern).
If you find yourself repeatedly using switch statements in the OOP paradigm, this is an indication that your classes may not be well design. Suppose you have a proper design of super and sub classes and a fair amount of Polymorphism. The logic behind switch statements should be handled by the sub classes.
For more information on how you are removed these switch statements and introduce the proper sub-classes, I recommend you read the first chapter of Refactoring by Martin Fowler. Or you can find similiar slides here http://www1.informatik.uni-wuerzburg.de/database/courses/pi2_ss03_dir/RefactoringExampleSlides.pdf. (Slide 44)
IMO switch statements are not bad, but should be avoided if possible. One solution would be to use a Map where the keys are the commands, and the values Command objects with an execute() method. Or a List if your commands are numeric and have no gaps.
However, usually, you would use switch statements when implementing design patterns; one example would be to use a Chain of responsibility pattern to handle the commands given any command "id" or "value". (The Strategy pattern was also mentionned.) However, in your case, you might also look into the Command pattern.
Basically, in OOP, you'll try to use other solutions than relying on switch blocks, which use a procedural programming paradigm. However, when and how to use either is somewhat your decision. I personally often use switch blocks when using the Factory pattern etc.
A definition of code organisation is :
a package is a group of classes with coherant API (ex: Collection API in many frameworks)
a class is a set of coherent functionalities (ex: a Math class...
a method is a functionality; it should do one thing and one thing only. (ex: adding an item in a list may require to enlarge that said list, in which case the add method will rely on other methods to do that and will not perform that operation itself, because it's not it's contract.)
Therefore, if your switch statement perform different kinds of operations, you are "violating" that definition; whereas using a design pattern does not as each operation is defined in it's own class (it's own set of functionalities).
I don't buy it. These OOP zealots seem to have machines that have infinite RAM and amazing performance. Obviously with inifinite RAM you don't have to worry about RAM fragmentation and the performance impacts that has when you continuously create and destroy small helper classes. To paraphrase a quote for the 'Beautiful Code' book - "Every problem in Computer Science can be solved with another level of abstraction"
Use a switch if you need it. Compilers are pretty good at generating code for them.
Use commands. Wrap the action in an object and let polymorphism do the switch for you. In C++ (shared_ptr is simply a pointer, or a reference in Java terms. It allows for dynamic dispatch):
void GameServer::perform_action(shared_ptr<Action> op) {
op->execute();
}
Clients pick an action to perform, and once they do they send that action itself to the server so the server doesn't need to do any parsing:
void BlueClient::play() {
shared_ptr<Action> a;
if( should_move() ) a = new Move(this, NORTHWEST);
else if( should_attack() ) a = new Attack(this, EAST);
else a = Wait(this);
server.perform_action(a);
}
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I'm currently working on a small 2D game-engine in C++, but I am now facing a daemon - I suck at designing a 'system of classes' that actually works. There are a blockade in my mind that disables me from seeing where I should use a class and where I should not. I was reading an article about engine-design and it purposed to use a 'State' class to manage the state of different game entries (I was using an int).
It also suggested that all objects for the game (not io/video/sound etc) derive from either Renderable or NonRenderable classes. That's smart. I already know that that was a smart way of doing it - I mean, every object in Java is of baseclass Object right? Smart, I know that! How come I didn't do it that way? What do I have to read to really get into this mindset?
Another example. I'm taking this summer-course in Ruby (really simple) and we're supposed to design a camping site. Simple! So, a camping is a collection of 'plots' that each have a electrical-gauge to measure how much power the guest has consumed. My design was three classes, one for a Camping - that in turn used arrays of Guest and Plot classes. My teacher suggested that I use more classes. WTF(!) was my first thought, where, what classes? Everything was a class in my opinion - until I realized, maybe the gauge should be a class to? Right now the gauge was an Integer in the Plot class.
I want to learn how to come up with a object oriented solutions to my problems - not just how to make the most obvious stuff into classes!
Tips/books/articles/blogs?
I'm two years into a collage degree in CS and have been programming as a hobby for many years! I'm 'just' stuck - and it's preventing me from creating any larger piece of software!
My personal experience was learning Object Oriented Software Construction with Object Oriented Software Construction, 2nd Edition by Bertrand Meyer.
The book was invaluable to me at that time, and still remains the single book from which I've learnt most regarding OO programming and software construction in general.
Here are some of its strong points:
In Part A: The issues, a very good definition of software quality.
In Part B: The road to object orientation, a logical, step by step search for OO techniques in a way that makes the reader think the investigation is being done live, that is, as if there were still no known results. You'll probably acquire the mindset you're looking for from this part.
In Part C: Object oriented techniques, the technical core of the book, you'll make your knowledge solid and learn very useful techniques regarding Design by Contract, Inheritance, Genericity, etc.
Part D: OO methodology: Applying the method well is a more practical approach on design, which I also find very useful. See for example How to find the classes (22), which you can find online.
After these parts, more advanced topics come, such as Concurrency (30) or Databases (31).
Since the book uses the Eiffel language (designed by the author), this will put you in the right mindset and teach you to think. It will be easy to apply these ideas to other, more or less OO, programming languages.
Object-oriented
Object-oriented programming is about asking objects to do something: a deceptively difficult concept to correctly apply.
Goban
Consider a 2D game board, like for playing Go (called a goban).
Think first about the behaviour it requires to accomplish its task. This means listing the behaviour for an object rather than deciding on data the behaviours manipulate. For example a basic board might have the following behaviours:
Place a Go stone.
Remove a Go stone.
Remove all the stones.
For a computer version of Go, it is convenient to bring attention to specific areas:
Mark an intersection (e.g., triangle, number, letter, circle, square).
Remove a mark from a marked intersection.
Remove all the marks.
Notice that a goban does not need to provide a way to provide clients with a reference to the stone at a specific intersection. Instead, it can answer questions about its state. For example, a goban might answer the following questions:
Is there a black stone at a given intersection?
Is there a white stone at a given intersection?
Is there a mark at a given intersection?
It is not the responsibility of the goban to know the state of the game: that belongs to an instance of a Game (which has Rules). In real life, a goban is simply a stage for stones.
At this point, we could write an interface for a goban without knowing how the underlying implementation will work.
public interface Goban {
public void place( Stone stone, Point point );
public void removeStone( Point point );
public void removeStones();
public void place( Mark mark, Point point );
public void removeMark( Point point );
public void removeMarks();
public boolean hasWhiteStone( Point point );
public boolean hasBlackStone( Point point );
public boolean hasMark( Point point );
}
Notice how the board is cleanly separated from both Rules and Games. This makes the goban reusable for other games (involving stones and intersections). The goban could inherit from a generic interface (e.g., a Board interface), but this should suffice to explain one way to think in terms of objects.
Encapsulation
An implementation of the Goban interface does not expose its internal data. At this point, I could ask you to implement this interface, write unit tests, and send me the compiled class when you have finished.
I do not need to know what data structures you have used. I can use your implementation to play on (and depict) a Goban. This is a crucial point that many projects get wrong. Many, many projects code the following:
public class Person {
private HairColour hairColour = new HairColour( Colour.BROWN );
public Person() {
}
public HairColour getHairColour() {
return hairColour;
}
public void setHairColour( HairColour hairColour ) {
this.hairColour = hairColour;
}
}
This is ineffective encapsulation. Consider the case where Bob does not like to have his hair coloured pink. We can do the following:
public class HairTrickster {
public static void main( String args[] ) {
Person bob = new Person();
HairColour hc = bob.getHairColour();
hc.dye( Colour.PINK );
}
}
Bob has now had his hair coloured pink, and nothing could prevent it. There are ways to avoid this situation, but people do not do them. Instead, encapsulation is broken resulting in rigid, inflexible, bug-ridden, and unmaintainable systems.
One possible way to enforce encapsulation is by returning a clone of HairColour. The revised Person class now makes it difficult to change the hair colour to Pink.
public class Person {
private HairColour hairColour = new HairColour( Colour.BROWN );
public Person() {
}
public HairColour getHairColour() {
return hairColour.clone();
}
public void setHairColour( HairColour hairColour ) {
if( !hairColour.equals( Colour.PINK ) {
this.hairColour = hairColour;
}
}
}
Bob can sleep soundly, knowing he will not awake to a pink dye job.
It pays to remember: OO is not an end in itself. The point of OO is to make development and, especially, maintenance of code easier over the lifetime of the product. Beware of the "OO for OO's sake" mindset.
Head First Object-Oriented Analysis and Design
I love Head First Books because they are fun to read. They have exercises and puzzles to scratch your head. I've read this book and found it very good.
The book covers:
Use OO principles (encapsulation and delegation)
Open-Closed Principle (OCP)
The Single Responsibility Principle (SRP)
Design patterns, UML, Use cases etc.
There are a blockade in my mind that disables me from seeing where I should use a class and where I should not.
When it comes down to it classes are a way to separate complex systems into simple parts that interact with each other. Try to create classes where otherwise you would be repeating yourself.
Right now the gauge was an Integer in the Plot class.
Does the gauge need to be a class? What would the advantage of turning it into a class be? These are the sort of things you always need to ask yourself.
Game Engines are difficult to design. The separation of such a vaguely defined requirements is a complex process, read here: article on game engines
Design is iterative and you'll refactor several times, don't be surprised by this.
There's an essay in the book "ThoughtWorks Anthology" by Jeff Bay: "Object Calisthenics" in which he gives a set of rules for designing OOP sofware:
Use only one level of indentation per method
Don't use the else keyword
Wrap all primitives and strings
Use only one dot per line
Don't abbreviate
Keep all entities small
Don't use any classes with more than two instance variables
Use first-class collections
Don't use any getters/setters/properties
On the first look it may look too strict to follow all these rules. Keep in mind that even trying to write some code that coplies them will make you better OOP designer.
Just remember there is never 1 solution to a problem.
Turning everything into a class is also not the solution. Especially tiny things (like the gauge) might very well be an int or float member inside the plot class like you had.
My suggestion is that practice is a good teacher. Just keep on trying, and keep on reading. In time you'll be more and more fluent.
I probably learned most about object oriented software development from Craig Larman´s Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development.
In his approach, classes are derived in a systematic way from use cases:
Nouns in the use cases are mapped to classes,
verbs to methods and
adjectives to member variables.
This, of course, works better for notions in the problem domain than, say, GUI widgets. Nevertheless, starting with a description/use case of the program to be written helped me to find better abstractions than when I omitted that step.
For me OO didn't 'click' until I read a book about design patterns. If you're already comfortable with concepts like abstract classes, interfaces, etc that's only half the battle.
The next step is figuring out why you should prefer composition over inheritance, how to code to an interface and how to write your classes so that they are decoupled and well-encapsulated. Design patterns show you solutions to common OO problems and help you structure your code adhering to the above guidelines.
I can't recommend any particular books about C++ but the GOF book is the standard on Design Patterns (Java). I prefer books that talk about design patterns in a particular language so that you can get concrete code examples. Design Patterns In Ruby is pretty good, as is PHP: Objects, Patterns and Practice.
I get the feeling that your instructor doesn't particularly know what he's talking about. 'More classes' is pretty useless advice by itself.
One of the things which helped to get into OO mindset, along with the practices that earlier posts outlined has been, is to rewrite/improving the existing code you have written using OO principles.
For example :
a. In situations where there is a lot of if/else constructs then probably
you can think of having a class hierarchy to distribute the branch codes accordingly,
and to use polymorphism.
b. Any use of operators like (instanceof in Java) would indicate programming to
concrete types and you can think how the instanceof check can be get rid of.
c. Use "Law of Demeter" as a guideline and see whether the coupling between
classes is high
To an extent the practice of "Test Driven Development" also helped me
since it forces you to think in terms of interfaces/behavior to be exposed
by a class rather than just concentrating on how best solution to a problem
can be coded.
Maybe you will find Thinking in patterns by Bruce Eckel useful. You can download this book from his site for free (I can only post one link as a new member, so just click on the links there and you can find it). Although, the book is from 2003, maybe the ideas presented in this book will help you grow as a programmer in general.
Write a really huge piece of software, and throughout the process, the bigger it gets, the more extensiblity you'll need and the more good class design you'll need, thus next time you'll think ahead and make your class design good in the beginning...
A simple way to come up with a reasonable set of things which probably should be objects (and hence, classes): write down a problem description of your task, like:
On a camping site there are guests, and each guest has access to a few outlets. The
software should be able to manage the power consumed by each guest, so it should know the
outlets used by a guest and the power consumed through each outlet.
Now, create a list of all the nouns for a good idea of what classes (= kind of objects) are involved in your problem:
Camping Site
Guest
Outlet
Power
This is not necessarily a definitive list, but it's a good start.
Haha. I remember that point. The whole "how the hell does this oo thing work?". Just keep at it, at some point it just clicks. It really is like a lightbulb going on. One moment it doesn't really make sense and then a split second later you're coding everything up in classes.
Try downloading some of the open source tools you will likely end up using and read the code. It'll give you something to reference your code style against.
Peter Coad and Ed Yourdon wrote a book about it couple years ago. While not filled with new overhyped methdologies, this book provides good foundation for thinking in object style.
In my opinion, one of the the best books I've read for learning object oriented concepts is:
The Object-Oriented Thought Process
For me, this book really gets you thinking in an object oriented way (well, the clue's in the title! :) It's fairly language agnostic, containing some small code samples throughout the text in VB.NET, C# and Java and frequently references many "greats" in the world of OO analysis and design, like Grady Booch, Martin Fowler and others.
Since the book helps you to think in an object oriented way, it'll often take a specific example and show the differences between the OO way of approaching a problem and the procedural way. This can be a big help if you're coming from more or a procedural background. It also touches on things like UML to help explain and understand the design behind complete libraries of classes (e.g. frameworks) and the interaction between classes and also the design of rich class hierarchies using concepts such as aggregation and composition.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
When designing a new system or getting your head around someone else's code, what are some tell tale signs that something has gone wrong in the design phase? Are there clues to look for on class diagrams and inheritance hierarchies or even in the code itself that just scream for a design overhaul, particularly early in a project?
The things that mostly stick out for me are "code smells".
Mostly I'm sensitive to things that go against "good practice".
Things like:
Methods that do things other than what you'd think from the name (eg: FileExists() that silently deletes zero byte files)
A few extremely long methods (sign of an object wrapper around a procedure)
Repeated use of switch/case statements on the same enumerated member (sign of sub-classes needing extraction)
Lots of member variables that are used for processing, not to capture state (might indicate need to extract a method object)
A class that has lots of responsibilities (violation of Single Repsonsibility principle)
Long chains of member access (this.that is fine, this.that.theOther is fine, but my.very.long.chain.of.member.accesses.for.a.result is brittle)
Poor naming of classes
Use of too many design patterns in a small space
Working too hard (rewriting functions already present in the framework, or elsewhere in the same project)
Poor spelling (anywhere) and grammar (in comments), or comments that are simply misleading
I'd say the number one rule of poor OO design (and yes I've been guilty of it too many times!) is:
Classes that break the Single
Responsibility Principle (SRP) and
perform too many actions
Followed by:
Too much inheritance instead of
composition, i.e. Classes that
derive from a sub-type purely so
they get functionality for free.
Favour Composition over Inheritance.
Impossible to unit test properly.
Anti-patterns
Software design anti-patterns
Abstraction inversion : Not exposing implemented functionality required by users, so that they re-implement it using higher level functions
Ambiguous viewpoint: Presenting a model (usually OOAD) without specifying its viewpoint
Big ball of mud: A system with no recognizable structure
Blob: Generalization of God object from object-oriented design
Gas factory: An unnecessarily complex design
Input kludge: Failing to specify and implement handling of possibly invalid input
Interface bloat: Making an interface so powerful that it is extremely difficult to implement
Magic pushbutton: Coding implementation logic directly within interface code, without using abstraction.
Race hazard: Failing to see the consequence of different orders of events
Railroaded solution: A proposed solution that while poor, is the only one available due to poor foresight and inflexibility in other areas of the design
Re-coupling: Introducing unnecessary object dependency
Stovepipe system: A barely maintainable assemblage of ill-related components
Staralised schema: A database schema containing dual purpose tables for normalised and datamart use
Object-oriented design anti-patterns
Anemic Domain Model: The use of domain model without any business logic which is not OOP because each object should have both attributes and behaviors
BaseBean: Inheriting functionality from a utility class rather than delegating to it
Call super: Requiring subclasses to call a superclass's overridden method
Circle-ellipse problem: Subtyping variable-types on the basis of value-subtypes
Empty subclass failure: Creating a class that fails the "Empty Subclass Test" by behaving differently from a class derived from it without modifications
God object: Concentrating too many functions in a single part of the design (class)
Object cesspool: Reusing objects whose state does not conform to the (possibly implicit) contract for re-use
Object orgy: Failing to properly encapsulate objects permitting unrestricted access to their internals
Poltergeists: Objects whose sole purpose is to pass information to another object
Sequential coupling: A class that requires its methods to be called in a particular order
Singletonitis: The overuse of the singleton pattern
Yet Another Useless Layer: Adding unnecessary layers to a program, library or framework. This became popular after the first book on programming patterns.
Yo-yo problem: A structure (e.g., of inheritance) that is hard to understand due to excessive fragmentation
This question makes the assumption that object-oriented means good design. There are cases where another approach is much more appropriate.
One smell is objects having hard dependencies/references to other objects that aren't a part of their natural object hierarchy or domain related composition.
Example: Say you have a city simulation. If the a Person object has a NearestPostOffice property you are probably in trouble.
One thing I hate to see is a base class down-casting itself to a derived class. When you see this, you know you have problems.
Other examples might be:
Excessive use of switch statements
Derived classes that override everything
In my view, all OOP code degenerates to procedural code over a sufficiently long time span.
Granted, if you read my most recent question, you might understand why I am a little jaded.
The key problem with OOP is that it doesn't make it obvious that your object construction graph should be independent of your call graph.
Once you fix that problem, OOP actually starts to make sense. The problem is that very few teams are aware of this design pattern.
Here's a few:
Circular dependencies
You with property XYZ of a base class wasn't protected/private
You wish your language supported multiple inheritance
Within a long method, sections surrounded with #region / #endregion - in almost every case I've seen, that code could easily be extracted into a new method OR needed to be refactored in some way.
Overly-complicated inheritance trees, where the sub-classes do very different things and are only tangentially related to one another.
Violation of DRY - sub-classes that each override a base method in almost exactly the same way, with only a minor variation. An example: I recently worked on some code where the subclasses each overrode a base method and where the only difference was a type test ("x is ThisType" vs "x is ThatType"). I implemented a method in the base that took a generic type T, that it then used in the test. Each child could then call the base implementation, passing the type it wanted to test against. This trimmed about 30 lines of code from each of 8 different child classes.
Duplicate code = Code that does the same thing...I think in my experience this is the biggest mistake that can occur in OO design.
Objects are good create a gazillion of them is a bad OO design.
Having all you objects inherit some base utility class just so you can call your utility methods without having to type so much code.
Find a programmer who is experienced with the code base. Ask them to explain how something works.
If they say "this function calls that function", their code is procedural.
If they say "this class interacts with that class", their code is OO.
Following are most prominent features of a bad design:
Rigidity
Fragility
Immobility
Take a look at The Dependency Inversion Principle
When you don't just have a Money\Amount class but a TrainerPrice class, TablePrice class, AddTablePriceAction class and so on.
IDE Driven Development or Auto-Complete development. Combined with extreme strict typing is a perfect storm.
This is where you see what could be a lot of what could be variable values become class names and method names as well as the gratuitous use of classes in general. You'll also see things like all primitives becoming objects. All literals as classes. Function parameters as classes. Then conversion methods everywhere. You'll also see things like a class wrapping another delivering a subset of methods to another class inclusive of only the ones it needs at present.
This creates the possibility to generate an near infinite amount of code which is great if you have billable hours. When variables, contexts, properties and states get unrolled into hyper explicit and overly specific classes then this creates an exponential cataclysm as sooner or later those things multiply. Think of it like [a, b] x [x, y]. This can be further compounded by an attempt to create a full fluent interface as well as adhere to as many design patterns as possible.
OOP languages are not as polymorphic as some loosely typed languages. Loosely typed languages often offer runtime polymorphism in shallow syntax that static analysis can't handle.
In OOP you might see forms of repetition hard to automatically detect that could be turned into more dynamic code using maps. Although such languages are less dynamic you can achieve dynamic features with some extra-work.
The trade of here is that you save thousands (or millions) of lines of code while potentially loosing IDE features and static analysis. Performance can go either way. Run time polymorphism can often be converted to generated code. However in some cases the space is so huge that anything other than runtime polymorphism is impossible.
Problems are a lot more common with OOP languages lacking generics and when OOP programmers try to strictly type dynamic loosely typed language.
What happens without generics is where you should have A for X = [Q, W, E] and Y = [R, T, Y] you instead see [AQR, AQT, AQY, AWR, AWT, AWY, AER, AET, AEY]. This is often due to fear or using typeless or passing the type as a variable for loosing IDE support.
Traditionally loosely typed languages are made with a text editor rather than an IDE and the advantage lost through IDE support is often gained in other ways such as organising and structuring code such that it is navigable.
Often IDEs can be configured to understand your dynamic code (and link into it) but few properly support it in a convenient manner.
Hint: The context here is OOP gone horrifically wrong in PHP where people using simple OOP Java programming traditionally have tried to apply that to PHP which even with some OOP support is a fundamentally different type of language.
Designing against your platform to try to turn it into one your used to, designing to cater to an IDE or other tools, designing to cater to supporting Unit Tests, etc should all ring alarm bells because it's a significant deviation away from designing working software to solve a given category of problems or a given feature set.