Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

A little help for a new OOP user

Status
Not open for further replies.

yea420

Programmer
Dec 7, 2004
4
CA
Hi everyone,

I'm having a bit of trouble wrapping my head around creating an OOP application. I've done the tutorials, bought the books and done numerous examples. However, I am not building anything that involves a customer, a user, a car and/or bike and certainly not an "ideal chair". What I am building is a lot of conversion tools (ie txt - InDesign, inDesgin - html, html - xml, etc.). So far I've been doing it successfully but the code would make you all cringe. Things are seperated by size rather than classes, etc. So here's the question, if one of you we're going to write a conversion tool from tagged text to html, what would your classes look like? The code isn't my problem, but if you could give me a couple of examples of the properties and methods that would send me in the right direction I'd be very thankfull.

Thank u all very much.
 
Ahhh, Hi Yea.. I love this one. It's called the strategy pattern.

Problem:
So basically you want to take a file in one format and convert, or encode it into another? Add to that, many conversion permutations are possible!

Some engineers new to oop might approach this with the infamous "select case" or "if....then" statements...

-=PSEUDO=-
if(file = "html" && converTo == "xml" {
...
...
}
else if...
else if..


Well, one thing I've have learned from this approach is that it is "open for extension and open for modification which is bad..Everytime a new conversion comes along, edits must be made to your class, this leads to restesting of the entire module and possible infection of new bugs...And as Robert C. Martin coined it, "code rot".

The OCP principal states a program should be open for extension but closed to modification..Your system should be designed to make conversion strategies interchangeable sort of like a plug-in like design..

2 months later, adding a new coversion strategy from [inDesign --> SOAP] should just be a new "strategy" class that needs to be written with specific knowledge for that particular conversion.

That's it, each class will encapsulate a single strategy for conversions..So, you'll end up with strategic converter classes, such as:

HtmlToXML
InDesignToHTML
HtmlToSoap
etc...
etc..

They should all implement a commant Interface, such as IConverter with the method "PreformConversion();"

With that said, I would model a Converter class that can accept an IConverter and a File and preforms a conversion using the converter u specify.


[Iconverter]<----[FileConverter]---->[FileSource]
^ ^ ^
| | |
| | |
[HtmlToXml] | [String]
|
|
[Client Code]


Here is an example of any client that will use your converter object:


String result = FileConverter.PreformConversion(new HtmlToXML(),"c:\myFile.html");


Does that work? I hope I understood your problem well..
- EdwardJS



 
My lines in the ascii diagram is off, but HtmlToXml implements Iconverter..

FileConverter uses just the interface of IConverter (preformConversion()), so FileConverter never needs to change.

File converter also associated to an inputer stream/string/bytes that it will operate on..

Internally, FileConverter calls PreformConversion() on IConverter passing in the input file to operate on...


 
Hi,

Thanks a bunch for the excellent explanation. I really appreciate it. Still absorbing the peripherals but it sounds like the answer I've been looking for.

Much appreciated
 
Hey EdwardJS024,

I've been working my way through your suggestions and am unclear on just one aspect. The IConverter class. Perhaps the best way for me to explain it is give the steps for how I understand your description:

1. Client calls the FileConverter Class passing it the SourceFile path and the Strategy class name

2. The FileConverter Class is responsible for reading the contents of the source file and passing this, along with the Strategy class Name to the IConverter Class

3. **This is where I'm a little confused** The IConverter class is responsible to passing the file information to the appropriate Strategy class, now the only way I can see doing this is some kind of Select Statement,

ie. Select Case StrategyClass
Case "XMLtoHTML"
new XMLtoHTML(fileContent)
'So on

but as you mentioned above I should stay away from such code as I would have to modify it each time I added a new Strategy class. I think perhaps my misunderstanding comes down to the line you wrote "implement a command Interface, such as IConverter" which I still am having a hard time understanding.

4. The appropriate logic to convert the file is performed by the Strategy class.



 
Hello yea420,
Edward actually helped me with the recently so I have an example that may help; Here it is in java!



public interface IConverter
{
public void performConversion(String fileToConvert);
}

public class InDesignToHtml implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific InDesignToHtml stuff here

}
}

public class HtmlToXml implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific InDesignToHtml stuff here

}
}



public class FileConverter implements IConverter
{
private IConverter iConverter;
public void setConverter(IConverter iConverter)
{
this.iConverter = iConverter;
}

public void performConversion(String fileToConvert)
{
this.iConverter.performConversion(fileToConvert);
}

}

public class ClientApp
{
public void main(String[] args)
{
FileConverter fileConverter = new FileConverter();
fileConverter.setConverter(new InDesignToHtml());
fileConverter.performConversion("C:\file.id");

fileConverter.setConverter(new HtmlToXml());
fileConverter.performConversion("C:\file.html");

}

}

its a little different but the concept is to implement the interface in ALL of your converters and then you can accept it in your handler class and then if you ever come up with a new one you just make sure you implement IConverter and then you can just call

fileConverter.setConverter(new YourNewConverterClass());
fileConverter.performConversion("C:\whatever.nc");

Edward, is this correct?

Spend like you don't need the money,
love like you've never been hurt and dance like no one's watching!
 
Hi zooxmusic,

Now I get it! Thanks to both of you for the help.
 
Zoo,
perfect implementation. I just want to clarify a few things. When I said that it's bad to utilize many "if...then" logic, I'll take that back...In some cases it is necessary.

You can never eliminate this type of choice logic, so it's best to isolate and minimize it....

Let's say the user will choose the conversion strategy to use from a front end GUI then you can imagine some code like:

if("xml") {
fileConverter.setConverter(new YourNewConverterClass());
}
else {
//
}
else {
}


So thats where "if...then" logic comes in and it's not that bad. I know it seems to defeat the whole purpose but you can totally eliminate "if then" logic with use of reflection...

EdwardJS
 
The approach could be made more flexible still if you separate the concepts of the format a 'document' is in (eg. XML, HTML) from how it is stored (eg. a file, a network socket stream etc). Suggest a slightly different interface (in Java):-
Code:
import java.io.InputStream;
import java.io.OutputStream;
public interface IConverter {
   public void performConversion(InputStream input, OutputStream output);
}

The converter strategy classes can then do their job without having to make assumptions about where the data to be converted is coming from, and where/how the results are to be stored.
You could maybe employ the Stragety pattern again to obtain the necessary implementation instances for the input and output parameters.
So say you had a conversion strategy to convert HTML to RTF (not a simple job by any means!) called HTML2RTF.
Then, if you wanted to convert HTML content at a URL location to a RTF file on the local drive you would only need to write a stragey to provide an InputStream from a URL and an OutputStream to an RTF file and the conversion stragegy class (HTML2RTF) would not change at all.
 
Yea,

The "if...then" is indeed necessary in the client code for the Strategy pattern. "Clients must be aware of different Strategies...before it can select the appropriate one." (Design Patterns, Gamma et al).

So it's really somewhat a drawback to the Strategy pattern. I think the same applies to the Template pattern as well.

I would like to know if someone has a good idea to avoid that. Perhaps through the use of polymorphism.

Cheers,

Ed
 
It may be possible to have 2 converters in the setConverter method.

1 to convert propriatry formats to a standard format and 1 to convert the standard format to a propriatry format. This reduces the number of classes you have to write.

public interface IConverter
{
public void performConversion(String fileToConvert);
}

public class InDesignToStandardFormat implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific InDesignToHtml stuff here

}
}

public class HtmlToStandardFormat implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific HtmlToStandardFormat stuff here

}
}

public class XmlToStandardFormat implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific XmlToStandardFormat stuff here

}
}

public class StandardFormatToInDesign implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific InDesignToHtml stuff here

}
}

public class StandardFormatToHtml implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific HtmlToStandardFormat stuff here

}
}

public class StandardFormatToXml implements IConverter
{

public void performConversion(String fileToConvert)
{

//do your specific XmlToStandardFormat stuff here

}
}


public class FileConverter implements IConverter
{
private IConverter fromConverter;
private IConverter toConverter;
public void setFromConverter(IConverter iConverter)
{
this.fromConverter = iConverter;
}

public void setToConverter(IConverter iConverter)
{
this.toConverter = iConverter;
}

public void performConversion(String fileToConvert)
{
this.fromConverter.performConversion(fileToConvert);
this.toConverter.performConversion(fileToConvert);
}

}

public class ClientApp
{
public void main(String[] args)
{
FileConverter fileConverter = new FileConverter();
fileConverter.setFromConverter(new InDesignToStandardFormat());
fileConverter.setToConverter(new StandardFormatToInDesign());
fileConverter.performConversion("C:\file.id");

fileConverter.setFromConverter(new HtmlToStandardFormat());
fileConverter.setToConverter(new StandardFormatToXml());
fileConverter.performConversion("C:\file.html");

}

}

This starts to pay off if you have 3+ types which all need to be interchangeable. For n formats, the method above requires n^2 IConverter classes whereas the second method requires 2n converter classes i.e with 4 formats, 8 classes instead of 16.

Craig
 
Craig,

This method certainly provides more flexibility and extensibility to a certain extent. However, this introduces more work, mostly in trying to decide what standard format will serve for all the formats now and the future. If the standard format somehow doesn't work with a particular (probably new) model, changes will need to be made to all conversion methods.

And the Client will still have to know the differences between the methods in order to make choices.

Cheers,

Ed
 
I really don't think it is possible to totally eliminate if..else or switch statements from the code. The idea is to isolate them in a method or class whose sole function is to decide which implementation of the interface should be used. Then the actual implementations are done in seperate classes as detailed already in this thread. This way, if you need to adda new implementation, you know that you only need to add conditional code to the one method whose job this is, and then you can write a new implementation of the interface which has no knowledge of other interfaces.

I hope this makes sense!
 
Below is a rough idea of how I might approach the problem in the way I just said. Any comments welcome!

Code:
public interface IConverter
{
     public void performConversion(String fileToConvert);
}

public class InDesignToHtml : IConverter
{

     public void performConversion(String fileToConvert)
     {

          //do your specific InDesignToHtml stuff here

     }
}

public class HtmlToXml : IConverter
{

     public void performConversion(String fileToConvert)
     {

          //do your specific InDesignToHtml stuff here

     }
}
  


public class FileConverter : IConverter
{
   private IConverter iConverter;
   public void setConverter(IConverter iConverter)
   {
       this.iConverter = iConverter;
   }

   public void performConversion(String fileToConvert)
   {
       this.iConverter.performConversion(fileToConvert);
   }

}

public enum ConversionTypes
{
     IndesignToHtml,
     HtmlToXml,
     File
}

public class Conversion
{
     public static IConversion GetType(ConversionTypes type)
     {
          IConversion retType = null;

          switch(type)
          {
              case ConvertionTypes.IndesignToHtml:
                   retType = new IndesignToHtmlConversion();
                   break;

              case ConversionTypes.HtmlToXml:
                   retType = new HtmlToXmlConversion();
                   break;

              case ConversionTypes.File:
                   retType = new FileConversion();
                   break;

              default:
                   throw new EXception("InvalidType");
                   break;
          }
          return retType;
     }
}

public class ClientApp
{
     static void Main(string[] args)
     {
          IConversion c = Conversion.GetType(ConversionTypes.HtmlToXml);

          c.performConversion(filename);

     }

}
 
jby1,

Yes this is the method that the thread has been saying. I don't think there is a way to get rid of the switch too.
Thanks for the neat demo.

This thread has been a very nice detailed example of the Strategy pattern.

Ed
 
Switch statements can be eliminated using introspection/reflection, with the cost of some preformance.

This is where you weigh your options. Do you want scalability over Speed? I usually say, oo technology is not about speed, but the performance gained through flexibility.


I recently had a similar issue in one of my plugin frameworks. I wanted developers to write their own plugins that will be read and loaded into my application at runtime..

The problem was, Plugins can be dynamically added / removed via xml config. Using the Activator.CreateInstance, i was able to solve this without one line of select/if..then statements.

Maybe you could apply the same for your conversion:




-=Pseudo code=-

public class Conversion
{
public static IConversion GetType(ConversionTypes type)
{
IConversion retType = null;
retType = (IConversion)Activator.CreateInstance(type.Name);

return retType;
}
}


No all you have to do is write new converters in the future and drop them into ur compiled bin.

Edward J.S
 
**Now all you have to do is write new converters in the future and drop them into your compiled bin.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top