The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Tough design question for an OOP Guru

I'm slowly learnning OOP. Several times, I've run into the situation described below.It seems like the Obj Oriented Programming approach will greatly complicate the program.


I've got 3 similar programs. (the first two are rewrites)


Program1
 - Has 3 Options which affect behavior.
 -Option screen that lets user select options
 -Excercise form that is affected by the options.

Program2
-Same as Program1 but one of the options has a different name and different behavior.  (Program1 shows a picture, Program2 shows a number on the screen).

So changes affect both reading data (number vs. file), rendering that data (number vs. picture) and options screen.

Program3
-Same as Program1 but has a 4th option.I affects behavior of the UI. (Instead of showing a text file, it shows *part* of a text file, with bits of the text replaced by "_". Shows "The ______is red" instead of "The apple is red"


HOW DO YOU REPRESENT THIS AS OBJECTS
I could have an Options class that handles the setting value, saving the value, and has Display Text to show the user when selecting it, as well as the type of control that should be used to render it.

That seems like 3x the work of having a ProgramType variable and handling the deviations with Select Case.
The OTHER benefit is that now things are configured where they are used. E.g., if I deciide that I need to change the order that settings are displayed in, I just go to that form, move a control, and viola. Done. AND I get to see the benefit immediately.  This greatly enables usability improvements.
If that had been done the way I described above, I'd need to add a "screen order" field to the Options settings.  And I'd need to reassign them in Program3 (so they render in a different order). Then I'd need to make sure that looks OK.


Is there another option?
Am I missing something in my OOP innocence?
Mr. Analogy Send private email
Saturday, October 21, 2006
 
 
> Am I missing something in my OOP innocence?

Oh I don't think you are that innocent :-)

I would ask yourself what are you trying to accomplish by making one thing that can act like three different things?

Perhaps try building the programs one by one. As you notice common behaviors that can be refactored out then do so. Don't force it ahead of time.
son of parnas
Saturday, October 21, 2006
 
 
>> HOW DO YOU REPRESENT THIS AS OBJECTS

Most applications are aggregations of several classes. Very few people try to model an entire application as a class in a top-down way.

>> I could have an Options class that handles the setting value, saving the value, and has Display Text to show the user when selecting it, as well as the type of control that should be used to render it.

You're doing a good job of identifying commonalities, but the problem will be getting this type of conceptual framework to work properly in real life with the per-instance behaviors that you want to handle.

When beginning OOP it is best to start small and relatively unambitiously. A UI control that also has a way to display related data that is related due to the application type is overly complex, in my experience.

A related problem of trying to define an all embracing class ahead of implementation is that the definition tends to be brittle. You didn't anticipate everything that you "need" in order for that class to be all three things you need it to be, so the definition starts to fall apart when you apply it.

>> That seems like 3x the work of having a ProgramType variable and handling the deviations with Select Case.

Yes, it would be.

>> The OTHER benefit is that now things are configured where they are used. E.g., if I deciide that I need to change the order that settings are displayed in, I just go to that form, move a control, and viola. Done. AND I get to see the benefit immediately.  This greatly enables usability improvements.

Possibly... but again, it sounds like you are counting on these compehensive classes being stable enough that you will have your own "IDE" like environment specific to your applications.

Son of Parnas had a good take on this. I will add to that - just start writing an application with the built in components, and see where that takes you.

A tangent now: you said you have two ways of displaying text from a file in different applications, but (I assume) they share some behavior and look and feel. I will suggest one good way to start your design is to design only a text-display component with common behaviors, and plan to initially "wire" it manually to the related UI controls in each application. In other words, JUST the text display, not the option buttons.
Bored Bystander Send private email
Saturday, October 21, 2006
 
 
>>Am I missing something in my OOP innocence?

Yes. You'd be better off separating first the "business logic" classes from GUI classes. Things will probably look much clearer then. Do not think about OO design by looking at the GUI. While it's easier in a way because you can visualize things better (they are already in front of your eyes) this can lead to a "wrong" design.

Of course, modeling objects for the business logic in a completely abstract way, with no GUI to look at, is hard. But it's the Right Thing To Do (tm). At firts, you won't be very good at it, but like with everything else, practice makes perfect.

I will finish the comment by repeating what Bored Bystander said: "Very few people try to model an entire application as a class in a top-down way."

I have seen this approach taken by a few guys and it never worked. Don't do this - it will never work. As Bored said, try working from smaller classes toward bigger ones, slowly working your way toward more complex designs. After a few years everything will fall into place.
Drazen Dotlic Send private email
Saturday, October 21, 2006
 
 
My proposed solution is at the END of this post.

REPLIES TO ABOVE RESPONSES
1. I don't think I made this clear enough in my original post: Program1 and Program2 *already* exist as described, in vb3.
I am trying to plan for a rewrite of those programs in VB.net, refactoring as I go. So, I'm very aware of the common behaviors Son of Parnas mentioned. I'm not trying to force this ahead of time. This is a Refactoring.


I'm trying to apply some OOP principles I've learned on a concrete example. The simplest example I could think of. The code that varies between these 3 programs is probably about 100 lines.


CLARIFICATION

Program1
 - Has 3 Options which affect behavior.
    -ShowText
    -SpeakModelSound
    -ShowPicture

 -Option screen that lets user select options
 -Excercise form that is affected by the options.

Program2
-Same as Program1 but one of the options has a different name and different behavior.  (Program1 shows a picture, Program2 shows a number on the screen).
    -ShowNumerals replaces ShowPicture
So changes affect both reading data (number vs. file), rendering that data (number vs. picture) and options screen.
But, if the behavior change makes it too difficult, just disregard that part. (I'm not looking for something I can use in a production program. I just want to get an idea of how I'd implement this in OOP programming.)

Program3
-Same as Program1 but has a 4th option.I affects behavior of the UI. (Instead of showing a text file, it shows *part* of a text file, with bits of the text replaced by "_". Shows "The ______is red" instead of "The apple is red"

* See note above about ignoring changes in Behavior if that makes it too hard.

2. Son of parnas:
-What I'm trying to accomplish is, primarily, is code reuse. This is about a 5,000 to 8,000 line program.  By changing about 1% of the code, I was able to create a second, comletely different program. Thus greatly increasing revenue and value to my customers, for minimal cost.

3. Bored Bystander:
"the problem will be getting this type of conceptual framework to work properly in real life with the per-instance behaviors that you want to handle."

Yep. That's exactly the problem.
As I mentioned, this is the simplest example I could think of. This is our simplest program (even simpler than the one we discussed earlier, Direction Following).
I'm not trying to model the entire program. I've just extracted one tiny piece that (I think) is simple, yet will excercise what I've learned about an OOP design.
I'm figuring if I can't design this tiny piece (1% of the program) with an OOP approach, then I start to have doubts about my ability to even understand an OOP approach. Or I have to conclude the OOP is overkill in a program this small. 

"A related problem of trying to define an all embracing class ahead of implementation is that the definition tends to be brittle. "

This is not meant to be 'all embracing'.  I've limited the scope to one tiny piece:  A list of 3 or 4 boolean options. (I.e., 3 or 4 boolean variables).And the  features *are* already implemented, but in procedural architecture, not OOP arch. Again, this is a Refactoring.

OK, Program2 has a different behavior for the option. Program1.ShowPicture becomes Program2.ShowNumerals.  Somewhere in another part of the program, we're going to get a  VisualCue string which is either a filename or numerals  ("apple.bmp" or "123").

I can see where that might complicate things. If so, disregard that. Just solving the other aspects is sufficient for my little example.

4. Drazen
"You'd be better off separating first the "business logic" classes from GUI classes."
This is all presentation layer stuff. I.e, these are option to let the user control what is shown in the GUI.
There are no business rules here.


MY SOLUTION
I'm going to ignore the Behavior changes, and focus on just showing and saving the settings.

Class: Options
  - list of options.  For each option we'll have
      -Name  (public, so we can reference it outside this class). This will probably be an enumerated data type.
      -ValueType (all boolean for now)
      -Description (natural language description shown to user when we let them choose the Option)
      -DisplayOrder (order shown on the screen)
      -TooltipText

Methods
    DisplayOptionsToUser
      - Displays the lIst of Options, using an appropriate control, using thier settings.
    SaveOptions
    LoadOptions


This class may USE  a class called Option if we intend to use Option elsewhere.


Program1 will have an OptionsClass
Program2 will inherit Program1.OptionsClass and override (or replace) DisplayPicture with DisplayNumerals.

USAGE
  Options.Load  (load previous options, or defaul values)
  Options.DisplayOptionsToUser  - shows form so user can pick options.

  If Options.GetOption(OptionShowText) then show text.

Is this sort of how one would do this in an OOP approach?
I realize that I've avoided the most complex part: having the program behavior change based on options selected. But this is a start.

I'm not looking for production code, just testing what I'm learning about OOP in a realworld example.
Mr. Analogy Send private email
Saturday, October 21, 2006
 
 
Seriously why have you drunk the OOP Kool Aid?
As a computer scientist you ought to be able
to think for yourself and not copy every
programming fad.
Objects Suck
Saturday, October 21, 2006
 
 
Haven't drunk the Kool-Aid, just doing a taste test.

I'm not scientist.
Mr. Analogy Send private email
Saturday, October 21, 2006
 
 
> What I'm trying to accomplish is, primarily, is code reuse.

Then we you see an opportunity to reuse code take it. It's not really that difficult. It just may be difficult to see all opportunities from the beginning of time.
son of parnas
Saturday, October 21, 2006
 
 
Son of parnas,

I'm not trying to reuse particular bits of code, I'm trying to reuse 99% of the code in one program to create another. (I've already done that about 15 times to end up with 20 programs. But that was all done procedurally with lots of "select case ProgramType..."

I.e., by taking a small part of Program1 and putting it in a class, I hope to be able to change JUST the CLASS(es) and end up with anothe program.

As Shalloway and (Trent?) say in thier book on design patterns, isolate the stuff likely to change into a class.  The benefit of OOP( they say, and I believe) is not reusing the class, it's changing the class so you an reuse everything ELSE.

I.e., if class is 1% of your code and you can change that 1% and thus reuse the other 99%, it's worth 3x the effort on writing that 1% of code as a class.

Now I just need to figure out how to do it <grin>.
Mr. Analogy Send private email
Sunday, October 22, 2006
 
 
If you are trying to shoehorn this problem into an object-oriented design, and it resists, it is a sure sign that the problem solution is not in objects. You won't learn much from solving it awkwardly, either. Find another problem or pick a sub-problem that will actually benefit from OOP.

P.S. OOP is not about code reuse; it is about encapsulation and polymorphism, in a nut shell. Code reuse should not be your top priority; think how OOP benefits the insides of your programs instead.
ping?
Monday, October 23, 2006
 
 
I recommend you read Martin Fowler and the some articles on Domain Driven Design.

Jimmy Nilsson has a new book out that will really help you grasp OOP as well.

I agree with Ping as well - this isn't about 'code reuse'.  To me, it's about craftsmanship - developing a good object oriented solution that is easy to unit test and understand.  Principles such as composition and encapsulation are the foundation of developing good software.
Recce
Tuesday, October 24, 2006
 
 
[[ I'm not trying to reuse particular bits of code, I'm trying to reuse 99% of the code in one program to create another. (I've already done that about 15 times to end up with 20 programs. But that was all done procedurally with lots of "select case ProgramType..." ]]

There's the split point. A case statement (or if/elseif/else chain) is a common indicator that separate classes might be appropriate. A base class would implement common behavior, and subclasses would implement the variant behaviors. If you find you're repeatedly doing 'select case' on the same (basically static) state variable, it's a very *strong* indicator. Martin Fowler, in Refactoring (ISBN 0-201-48567-2), calls this a "bad smell." See the "Replace Type Code With Class" refactoring.

In an earlier message, you said you're working in VB3. I use C++ or Python, so the exact mechanism will differ. (I have no idea how you would do these in VB, as I don't use VB.)

In C++ or Python, one might...

1. Create a base class that does all the common stuff, and invokes a virtual function to do the program-type-specific stuff. If there is a "default" behavior for this function, implement it in the base class; otherwise, make it pure-virtual (in C++) or raise NotImplementedError (in Python) in the base class.

2. Derive a subclass for each program type that diverges from the default behavior, implementing the appropriate behavior in the overridden function. (For convenience/readability, you could also create subclasses for program types the *do not* diverge from the default behavior; this would provide a distinct subclass for each program type. In C++, a simple typedef would suffice. In either language, the additional overhead would be minimal.)

3. In each main program, instantiate the appropriate subclass.

HTH.
Samuel Reynolds Send private email
Wednesday, October 25, 2006
 
 
My comment, 5 days later oh well.

The idea of making the options which drive your options interface and storage to file seems like a good fit.

I think it would be a missstep to then directly relate that object structure to how those options apply to the rest of the application.

Your options should initialise a 'renderer' object.  The renderer object (or perhaps objects) could make use of some poymophism. 

For instance - I'll assume 'option 2' turns on or off the option to display the extra information. And secondly it can be different between the programs.

IDisplayThingamy
{
  DrawIT();
}

NullDisplayThingamy: IDisplayThingamy
{
  DrawIT(){return}
}

NumericDisplayThingamy: IDisplayThingamy
{
  DrawIT(){print number, return}
  NumericDisplayThingamy(int i){number=i}
  int number
}

ImageDisplayThingamy: IDisplayThingamy
{
  DrawIT(){draw image, return}
  ImageDisplayThingamy(string file){ read file into image, return }
  data image
}

Then you have the bit of code that ties the chosen options to the creation of the objects - depending on the languaage would depend on the possible methods, factory paattern is normally the go.

Program 1
IDisplayThingamy dispThing
If Option 1 set
  dispThing = new ImageDisplayThingamy('somefile')
else
  dispThing = new NullDisplayThingamy()

and through the program you just call
  dispThing.DrawIT();

Program 2 you only change the binding, the main program doesn't need to change, neato

Program 1
IDisplayThingamy dispThing
If Option 1 set
  dispThing = new NumericDisplayThingamy(1)
else
  dispThing = new NullDisplayThingamy()


Ok, so next thing you say is that the data isn't static. 
So you define a heirarchy for your data provider, pass around IData, and so on...
Gornin Send private email
Tuesday, October 31, 2006
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz