locked
Factory Pattern questions. RRS feed

  • Question

  • Hi Everyone..

    Im currently working on a project where I need to be able to read multiple types of live feeds.. The feeds contain advertisements. Anyway some of the feeds are xml, some are plain text delimited by the pipe symbol, some are comma delimited text files and some are csv files.

    When adding the feeds to our database I will be selected which type of feed it is, the url of the feed ect..

    Ive done some research and this seems like a good candidate for the factory pattern.

    Here is where im lost.

    Public Interface IReader
    
      Function Read(feed As Object) As List(Of Advertisement)
    
    End Interface
    

    I used Object because I didnt know what to put there..

    Also, dont quite know how to structure the solution.

    I have one project in the solution called Core will hold the core classes such as Advertisement, ect..

    I have another winforms project that will do the processing, ect.

    Should I make a new class library project for all the different types of FeedReaders and the factory classes?

    Also, im confused as to what I should be passing to the reader itself. The location of the data, the data itself, ect.. ????

    Since all readers will be reading different formats im kind of lost what the input should be.

    If anyone could give me a basic example to get me going i would greatly appreciate it.

     

    Monday, June 20, 2011 12:06 AM

Answers

  • Ok. this happens as more information comes along but again think it best to change the design. Instead of creating different readers for different file types it's better  to create one reader that uses different parsers, Different parser for a file format. So it's no longer about the file type but the format of the data in a file; regardless of it's type. The strategy now becames the parsers.

    A new format comes along then create a new parser for it. Actually going back to the original post , the factory pattern can be used to return the correct parser (strategy) for whatever file type.

    Is a bit of a change again but that's what it's all about, refinement....   :)

    Ok so back to the code.... I'd want to write this sort of code for the main method.

                FileInfo sourceFile = new FileInfo("source_file.csv");
                FileInfo destinationFile = new FileInfo("destination_file.xml");

                AdvertismentReader reader = new AdvertismentReader(sourceFile);
                AdvertismentWriter writer = new AdvertismentWriter(destinationFile);

                IEnumerable<Advertisment> advertisments = reader.Read();
                writer.Write(advertisments);

    Not much change there. The IReader and IWriter interfaces come out as they aren't needed anymore. It's just a reader and a writer.

    Lets just concentrate on the reader side because thats the bit that deals with the data format..

    namespace FileConverter.IO
    {
        using System.Collections.Generic;
        using System.IO;
        using Parsers;

        internal class AdvertismentReader
        {
            private readonly FileInfo source_file;

            public AdvertismentReader(FileInfo sourceFile)
            {
                source_file = sourceFile;
            }

            public IEnumerable<Advertisment> Read()
            {
                IParser parser = ParserFactory.Get(source_file);

                string contents = File.ReadAllText(source_file.FullName);
                return parser.Parse(contents);
            }
        }
    }

    OK the Read method uses a ParserFactory... this factory looks at the source file, determines the correct parser for the data format in the file, and returns it.

    The contents of the file is then read and parsed.... the factory would be something like this....

     

    namespace FileConverter.Parsers
    {
        using System.IO;

        class ParserFactory
        {
            public static IParser Get(FileInfo file)
            {
                if (file.Extension == ".csv")
                    return new CommaDelimitedParser();

                return new NullParser();
            }
        }
    }

    in this case the file extension is used and it's determined that a CommaDelimiterParser is needed to parse that file. It doesn't need to be based on file extension it could be based on the files content.... anything. As long as the factory con determine which parser to use for each format then it should all work. NullParser is a helper class that means if the farmat is unknown the program won't crash.

    namespace FileConverter.Parsers
    {
        using System.Collections.Generic;

        internal interface IParser
        {
            IEnumerable<Advertisment> Parse(string contents);
        }
    }

    Any parser that implement the above interface will work with this approach.

     


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    • Marked as answer by ntek designs Thursday, July 7, 2011 1:51 AM
    Wednesday, July 6, 2011 12:30 PM
  • Your welcome nTek, I like these sorts of questions. Think the solution will go a long way. There was talk of the application downloading the 'feeds' and that feature can still go in without needing to change very much of what's there. A change in design would be minimal. You can see that the code at the moment works only with FileInfo objects so it's geared towards files only, don't sweat it, but for downloads that can be improved and well actually I was going to leave it but might as well tell you about that now....

    The idea would be to add the concept of the reader reading from a 'data source' rather than a file.  Here's the idea in code.

                IDataSource source = new UriDataSource("http://www.feeds.com/data.rss");
                FileInfo destinationFile = new FileInfo("destination_file.xml");

                AdvertismentReader reader = new AdvertismentReader(source);
                AdvertismentWriter writer = new AdvertismentWriter(destinationFile);

                IEnumerable<Advertisment> advertisments = reader.Read();
                writer.Write(advertisments);

    Interface of IDataSource used instead of just FileInfo.. Simple interface with a GetData method....

    namespace FileConverter.DataSources
    {
        internal interface IDataSource
        {
            string GetData();
        }
    }

    Here is an example of a, not all that good, UriDataSource

    namespace FileConverter.DataSources
    {
        using System.Net;

        internal class UriDataSource : IDataSource
        {
            private readonly string uri;

            public UriDataSource(string uri)
            {
                this.uri = uri;
            }

            public string GetData()
            {
                WebClient web = new WebClient();
                return web.DownloadString(uri);
            }
        }
    }

    and one for a file based data source....

    namespace FileConverter.DataSources
    {
        using System.IO;

        class FileDataSource : IDataSource
        {
            private readonly string path;

            public FileDataSource(string path)
            {
                this.path = path;
            }

            public string GetData()
            {
                return File.ReadAllText(path);
            }
        }
    }

     

    The reader then uses the interface to get the data at the data source and gets the parser for it as normal...

    namespace FileConverter.IO
    {
        using System.Collections.Generic;
        using DataSources;
        using Parsers;

        internal class AdvertismentReader
        {
            private readonly string contents;

            public AdvertismentReader(IDataSource source)
            {
                contents = source.GetData();
            }

            public IEnumerable<Advertisment> Read()
            {
                IParser parser = ParserFactory.Get(this.contents);
                return parser.Parse(this.contents);
            }
        }
    }

     

    The code in the parser factory now only works exclusively with the files content... but that is only in this examlpe, the DataSource could give clues as to what parser is needed... not sure about the last bit there but the idea was to show you how the design can be updated to get data from other sources rather than just files.  Keep the parsers and data sources seperate, don't for example have a parser per data source. Don't use one to determne the other although you could use clues from one to determine the other, if that makes sense.

     

    Used to have a blog and a wiki and used to spend a great deal of personnal time and effort investigating things and posting about them. Used to have my face in code 24/7. Then something very sad happened to someone I know who was just ike me and now I don't do it anymore.  That's why I didn't take you up on your offer.  Would have done before but now my personnal time is not software time. I digress.

     

    No worries at all about the help. Anything else I can help you with come find me on the forum.  take it easy.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    • Marked as answer by ntek designs Thursday, July 7, 2011 1:53 PM
    Thursday, July 7, 2011 9:24 AM

All replies

  • Hello sexy boys ,

     

    Miss. Eleanor does not think it is a good idea to use factory design pattern. Here the focus is of two objectives.

     

    1) Getting the feed from a live source , with multiple selection for the feed. It would be better if you use facade design pattern. The reason is that facade design will allow selection of one feed from multiple feeds. Behind the user interface it all comes how you implement facade design pattern. Please do a thorough proof of concept on this. The first point of attack how you implement the facade design pattern.

     

    In simple terms , each class the contains the required information that is represented to the user. How to control each of these under one main class based on the user selection will be the key of attack or focus.

     

    Test thoroughly this approach , before you move  forward.

     

     

    2)After getting right selection based on the user interface , you need to send the requried information to the database.  Here comes the focus of dal layer and database. You can use entity framework 4.0 for the dal layer. Then comes the database. Hope that you have a good database design.

     

    Cheers and Good Luck.

     

    Miss. Eleanor

     

     


    Monday, June 20, 2011 6:13 AM
  • Hey,

    Na the best pattern for this is the strategy pattern.

    Basically each of the different feed mechanisms will do the same thing (I mean the top level abstract thing) but it's just the way in which they do it that changes.

    So say as an example: the process is to

    download the feed,

    parse the feed,

    display the feed

     

    Ok so you can do the no matter how the feed is described as long as you create different 'strategies' for the above that allow the above to run regardless.

    I'd use the strategy pattern or failing that the template pattern.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Monday, June 20, 2011 10:36 AM
  • Do check do check the two approaches and see which one will meet your software funcitonal requirements.

    Do remember ego  is the element of ultimate destruction which benefit no one. As simple as that. 

    The aim and focus should be given the prime importance under what ever the situations maybe , and see how the objective can be achieved. 

    Monday, June 20, 2011 10:44 AM
  • How's each of the feeds kicked off?

    Are you listening for an event ( implied by "live" ) or nightly processing the same files you get via ftp or what?

    I would think each of the operations is sufficiently unique that the factory pattern would just complicate things

    It seems quite likely that each ought to remain separate processes.

    If it's just a case of different delimiers in some cases then one piece of code with an if substituting what the delimiter is could well work.

    But I would think xml is best handled in a completely different way.

    Do they even contain the same data going to the same place?

    Monday, June 20, 2011 10:56 AM
  • It's like allowing the application to open different file types.

    The process is the same regardless of the file type; show the dialog, open the file, read the file, close the file.

    Where it differs is the read the file bit. However, that's when it gets specific and that's why you use different strategies. The process remains the same but you swap in and out a different strategy depending on the type of file being read.

    The problem is the same as with reading feeds. The process is the same you just swap out strategies for the format of the feed.

    A new format of feed means a new strategy.

     

    Yeah just an idea that can be ignored if you want. Makes no difference to me.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Monday, June 20, 2011 11:09 AM
  • Thanks Everyone for your replies.

    The feeds will be coming in from an FTP which I will download nightly to my machine..  Eventually I would like to read the feed directly from the net but thats not a requirement as of right now.

    The process is the same for all different feeds as Derek said..

    Open the feed, read the feed, display the feed.

    So does everyone agree that the Stratagies pattern is the way to go? 

    Monday, June 20, 2011 2:16 PM
  • Factory and strategy are pretty close when you get down to it.

    So you could easily define an interface which takes a string, works out which is what using a process method and outputs a typed list.

    Implement this in two classes.  ( One of which also allows you to set a parameter for the delimiter. )

    One takes xml, the other parameterised fields.

    The two different strategies are implemented in your process method and created by a factory class.

     

    myList<T> = myconcrete.Process(infilestring);

     

    Of course if your database is sql server then maybe all this is academic as you should be using SSIS.

     


    Monday, June 20, 2011 3:21 PM
  • Sorry I should have been more specific on the file types..

    Im reading 3 different file types..   CSV, XML, TXT (delimited by pipe symbol OR comma) and as of right now I will be doing a nightly processing deal (download files from FTP and process them) eventually I would like the read the feeds from the net.

    I still dont quite understand your example.. I'll read up more on the strategy pattern tonight.

     

     

    Tuesday, June 21, 2011 1:28 AM
  • Hi,

    Here is some example code that shows how the strategy pattern can be used to do this; this was thrown together in a couple of minutes so take it with a pinch of salt.

    In this example I've decided that the way the feed is opened and the way the feed is displayed is also a strategy. The way the feed is opened could use the strategy pattern but not sure about the display.... up to you.

    Ok create interfaces that describe the strategies

        interface IFeedOpener
        {
            Feed Open();
        }

        interface IFeedReader
        {
            void Read(Feed feed);
        }

        interface IFeedDisplayer
        {
            void Display(Feed feed);
        }

    Then create a class that represents the process and which will uses instances of the above strategies.

        class FeedManager
        {
            IFeedOpener opener;
            IFeedReader reader;
            IFeedDisplayer displayer;

            public FeedManager(IFeedOpener opener, IFeedReader reader, IFeedDisplayer displayer)
            {
                this.opener = opener;
                this.reader = reader;
                this.displayer = displayer;
            }

            public void Process()
            {
                Feed feed = opener.Open();
                reader.Read(feed);
                displayer.Display(feed);
            }
        }

    Like that as an example. Now you have the a totally adaptable process that con mix and match openers readers and, if you want, displayers.


        class FileBasedFeed : IFeedOpener
        {
            public Feed Open()
            {
                throw new NotImplementedException();
            }
        }

        class AtomBasedFeed : IFeedOpener
        {
            public Feed Open()
            {
                throw new NotImplementedException();
            }
        }



        class XMLFeedReader : IFeedReader
        {
            public void Read(Feed feed)
            {
                throw new NotImplementedException();
            }
        }

        class CSVFeedReader : IFeedReader
        {
            public void Read(Feed feed)
            {
                throw new NotImplementedException();
            }
        }



        class ConsoleDisplayer : IFeedDisplayer
        {
            public void Display(Feed feed)
            {
                throw new NotImplementedException();
            }
        }

    Then you can mix and match depending on whatever situations you need to work with.

                FeedManager manager =
                    new FeedManager(new FileBasedFeed(),
                                                   new XMLFeedReader(),
                                                   new ConsoleDisplayer());
                manager.Process();

     

    Processing a feed from an XML file and displaying it on the console.

     

     

     

     

     

     

     


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Tuesday, June 21, 2011 7:13 AM
  • Derek, you my friend are awesome!  Thank you!!

    What does the Feed class look like though?  Thats where im confused. 

    I appreciate all your help.


    • Edited by ntek designs Tuesday, June 21, 2011 10:04 PM typo.. lol was yelling my kids name and typed it too.. <geek>
    Tuesday, June 21, 2011 10:02 PM
  • Very Good Derek Smyth....

    Good Luck.
    Wednesday, June 22, 2011 5:04 AM
  • Hi,

    As always the devil is in the detail.

    The Feed class was just a placeholder demonstrating that some way was needed to represent a Feed and that representation could be generated by and passed to each strategy as part of the process. What shape that representation takes will depend on the project and what makes up a Feed. You talked about advertisments in your initial post so perhaps thats the representation that's needed rather than the concept of a Feed; thing is I cannot really advise on that without more information on the project. However, I'm sure your able to work out the details now that an approach looks like a potential way forward.

    If there is a general way to represent the information about a feed then that's what the Feed class represented.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Wednesday, June 22, 2011 7:43 AM
  • Very Good Derek Smyth....

    Good Luck.

    Cheers. I'm not big on ego miss.eleanor. When there is nothing to gain and nothing to lose then ego doesn't play a part. Take it easy and I'll see you around.

    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Wednesday, June 22, 2011 7:48 AM
  • Thanks Derek.. I really appreciate your time and effort helping me with this..

    I am unable to contact you directly thru the forums, Any chance you'd have some free time and want to make some money?

    Shoot me your email if your interested.

    Thanks again man I really appreciate it!

    Thursday, June 23, 2011 1:37 AM
  • Hi,

    Happy to help, no worries.

    Not really looking for any extra work at the minute. Time is too precious to spend it all coding. Just noticed my profile says that I'm looking for work but that was really a more permanent position rather than something in my spare time.  Thanks for asking though.

     


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Friday, June 24, 2011 7:18 AM
  • Derek

    I like your approach using the Strategy Pattern and ive been trying to implement this into my project but im still confused.

    Im confused with a few things..   The Feed itself.. I dont know how to represent this. 

    The feed at its most basic representation is just a file.  The file type can be txt, xml, csv. The feeds all contain advertisements.  Each advertisement in the feed will be wrapped in an html template and saved to an XML file for later processing by another application.

    So, basically I am unsure as to how the Feed itself should be represented.   Should it just be a class with a FileName property? Or what else? Should I have a List(Of Advertisement) property that I can load with Advertisements after I parse the feed?

    Then, say I get the feed representation figured out the Feed Opener is the next thing that confuses me. Do I really need it?

    If so, how do I represent an Open Feed? 

    I will be using a StreamReader to open and read everything into a string and then processing them accordingly.  For instance, I will load an XML file into a string, then use the XMLDocument.Load(myString) to process the xml file; Then the txt or csv files will get opened and split into a string array and processed.

    Then the displayer. I dont think I need this part as because all the feeds get displayed the same way.  They get wrapped in the specified html template, and previewed in an webbrowser control.

    Thanks again for your time.

    -nTek

    Saturday, July 2, 2011 7:23 PM
  •  

    Hey man,

    "The file type can be txt, xml, csv. They [the files] all contain advertisements.  Each advertisement in the [file] will be wrapped in an html template and saved to an XML file for later processing by another application."

    ok this sounds more like a kind of a file converter rather than the application I had originally thought; an RSS Feed reader. That original thought was based on an assumption around the word feed. Think it would be best to forget the idea of a feed for now and instead just think in terms of converting the contents of one file into a common format which is then stored in another file. The conversion is really just selecting each advertisment in the source file and writing it an XML file.

    Lets change the design a little, well is the design changing or is it just the terminology, not sure... it's really just a reader and writer you need, but the writer will remain the same because it's the same format that is being written regardless of the reader.

    The code I'd want to write code for something like this would be...

                FileInfo sourceFile = new FileInfo("source_file.csv");
                FileInfo destinationFile = new FileInfo("destination_file.csv");

                IAdvertismentReader reader = new AdvertismentCsvReader(sourceFile);
                IAdvertismentWriter writer = new AdvertismentXmlWriter(destinationFile);

                IEnumerable<Advertisment> advertisments = reader.Read();
                writer.Write(advertisments);

    The readers and writers are still strategies. Well there is only one writer strategy.... the common XML format.... while there would be different readers for the different formats.

     

    namespace FileConverter
    {
        using System.Collections.Generic;

        internal interface IAdvertismentReader
        {
            IEnumerable<Advertisment> Read();
        }
    }

    namespace FileConverter
    {
        using System.Collections.Generic;

        internal interface IAdvertismentWriter
        {
            void Write(IEnumerable<Advertisment> advertisments);
        }
    }

     

    namespace FileConverter
    {
        using System.Collections.Generic;
        using System.IO;

        internal class AdvertismentCsvReader : IAdvertismentReader
        {
            private readonly FileInfo source_file;

            public AdvertismentCsvReader(FileInfo sourceFile)
            {
                source_file = sourceFile;
            }

            public IEnumerable<Advertisment> Read()
            {
                List<Advertisment> advertisments = new List<Advertisment>();
                
                // read the CSV in and for each advertisment create and add advertisment object

                return advertisments;
            }
        }
    }

     

    namespace FileConverter
    {
        using System.Collections.Generic;

        internal class AdvertismentTxtReader : IAdvertismentReader
        {
            public IEnumerable<Advertisment> Read()
            {
                List<Advertisment> advertisments = new List<Advertisment>();

                // read the TXT in and for each advertisment create and add advertisment object

                return advertisments;
            }
        }
    }

     

    namespace FileConverter
    {
        using System.Collections.Generic;
        using System.IO;

        internal class AdvertismentXmlWriter : IAdvertismentWriter
        {
            private readonly FileInfo destination_file;

            public AdvertismentXmlWriter(FileInfo destinationFile)
            {
                destination_file = destinationFile;
            }

            public void Write(IEnumerable<Advertisment> advertisments)
            {
                // for each advertisment write to destination file
            }
        }
    }

     

     


     

     

     

     

     


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Monday, July 4, 2011 11:57 AM
  • Im absolutely speechless right now man.  Thank you.  

    Sorry about the misuse of the term "feed", i can see how i made it more confusing.

    Anyways, thats a perfect start man and I really appreciate your time and help on this.   Absolutely the best experience ive had on this site yet.


    While I have you here id like to pick your brain on a few more things, if thats alright?

    The one detail that I forgot to mention is that the files may be formatted differently.  For example, one of our files that comes in contains 30 fields, in a specific order. And another one could come in at 25 fields, in a different order.   The advertisement class is the same for both, just some fields dont get filled.

    Do you think I'd have to create a different Reader for each of those different formats or what?

    Thanks again man I really appreciate your help.

    -nTek

    Monday, July 4, 2011 6:08 PM
  • Hi,

    No problems with the help. Some programmers don't really give much consideration to the form and structure of their applications and just hack away ending up with a big pile of mess; and that's when they ask for help; soon as it's too late. Good to see someone posting for help on the subject at the start, actually appreciating that thinking about the design a little up front is important and then asking. Cool. Happy to help.

    The question about the different formats is an interesting one. It's difficult to answer without seeing examples of the formats. Also it depends on what you mean by format. This might be a little difficult to explain but files that have different layouts (for want of a better word) could have the same format. Think an example is needed to explain this right. For me the following information is in the same format but has different layouts, it should be possible to read both of these with one reader.

    name    : JJ
    dob     : 20/6/74
    address : whatever
    acc     : 42602


    acc     : 42602
    name    : JJ
    address : whatever
    dob     : 20/6/74

    The format is the same, the data is in the same format of 'key : value' but the layout is different. The reader in this case is geared up for searching for keys in the file and then extracting the values. The order of the data isn't important and if the key isn't found then it can be handled ok.

    I won't insult your intelligence by posting an example were the format is different.

     

    Anyway should you write different readers for different formats of the same file type.... well the thing with that idea is how can the correct reader be created for the correct format of a file type. For example if you know the file is TXT then you know what reader is used. If there are many types of TXT reader then how can you tell which one to use for a specific TXT file. Well the answer is you need to read a little bit of the TXT file first.

    With that in mind what I would do is have one TXT reader that had a bit of intelligence behind it so that when you gave it a TXT file it could use the right code for parsing whatever format it was in.

    Something like this perhaps:

    namespace FileConverter
    {
        using System.Collections.Generic;

        internal class AdvertismentTxtReader : IAdvertismentReader
        {
            public IEnumerable<Advertisment> Read()
            {
                List<Advertisment> advertisments = new List<Advertisment>();

                // read enough of the TXT file to determine how to parse
                // call a method that reads the TXT format in
                //     and creates for each advertisment an advertisment object
                //     that is added to the list

                return advertisments;
            }

        }
    }

     

    The question then becomes should you call a method that reads the format? or do you create and use a Parser class? Post the formats, if you can, and I'll have a look.

     



    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Tuesday, July 5, 2011 8:10 AM
  • Thanks Derek!! 

    Like you said, I figured it would be best to do some research before i just started throwing stuff together and ending up with an unmanagable mess. Been there, done that.


    Here is the first format.

    Year|Make|Model|Engine|Price|2006|Toyota|Tacoma|V8|19,999|2009|Ford|F350|14,999


    Thats a shortened version, that particular file contains 23 field for each advertisement, with two of the fields containing a comma delimited list. For example, the options list would come in like this. (not that thats really relevant to the question, just figured id share that bit anyway)

    Year|Make|Model|Engine|Price|Options|2006|Toyota|Tacoma|V8|19,999|AC,Power Steering,Backup Camera|

    I dont have a second type of file/format coming in yet, I just want to be prepared because I know I will shortly.

     

    Thanks again buddy for your time. Its very much appreciated!! 

    Tuesday, July 5, 2011 1:48 PM
  • Ok. this happens as more information comes along but again think it best to change the design. Instead of creating different readers for different file types it's better  to create one reader that uses different parsers, Different parser for a file format. So it's no longer about the file type but the format of the data in a file; regardless of it's type. The strategy now becames the parsers.

    A new format comes along then create a new parser for it. Actually going back to the original post , the factory pattern can be used to return the correct parser (strategy) for whatever file type.

    Is a bit of a change again but that's what it's all about, refinement....   :)

    Ok so back to the code.... I'd want to write this sort of code for the main method.

                FileInfo sourceFile = new FileInfo("source_file.csv");
                FileInfo destinationFile = new FileInfo("destination_file.xml");

                AdvertismentReader reader = new AdvertismentReader(sourceFile);
                AdvertismentWriter writer = new AdvertismentWriter(destinationFile);

                IEnumerable<Advertisment> advertisments = reader.Read();
                writer.Write(advertisments);

    Not much change there. The IReader and IWriter interfaces come out as they aren't needed anymore. It's just a reader and a writer.

    Lets just concentrate on the reader side because thats the bit that deals with the data format..

    namespace FileConverter.IO
    {
        using System.Collections.Generic;
        using System.IO;
        using Parsers;

        internal class AdvertismentReader
        {
            private readonly FileInfo source_file;

            public AdvertismentReader(FileInfo sourceFile)
            {
                source_file = sourceFile;
            }

            public IEnumerable<Advertisment> Read()
            {
                IParser parser = ParserFactory.Get(source_file);

                string contents = File.ReadAllText(source_file.FullName);
                return parser.Parse(contents);
            }
        }
    }

    OK the Read method uses a ParserFactory... this factory looks at the source file, determines the correct parser for the data format in the file, and returns it.

    The contents of the file is then read and parsed.... the factory would be something like this....

     

    namespace FileConverter.Parsers
    {
        using System.IO;

        class ParserFactory
        {
            public static IParser Get(FileInfo file)
            {
                if (file.Extension == ".csv")
                    return new CommaDelimitedParser();

                return new NullParser();
            }
        }
    }

    in this case the file extension is used and it's determined that a CommaDelimiterParser is needed to parse that file. It doesn't need to be based on file extension it could be based on the files content.... anything. As long as the factory con determine which parser to use for each format then it should all work. NullParser is a helper class that means if the farmat is unknown the program won't crash.

    namespace FileConverter.Parsers
    {
        using System.Collections.Generic;

        internal interface IParser
        {
            IEnumerable<Advertisment> Parse(string contents);
        }
    }

    Any parser that implement the above interface will work with this approach.

     


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    • Marked as answer by ntek designs Thursday, July 7, 2011 1:51 AM
    Wednesday, July 6, 2011 12:30 PM
  • Derek,  I can't thank you enough for your time and help on this..

    That is perfect! Exactly what I need.  It would have taken me forever to figure all that out.

    Im going to run with this approach and see how everything turns out..  If I have any more issues, ill post back here. 


    Just curious, do you have a blog or anything?

    Anyway, Thanks again man.. I reallly appreciate your time and help.  I owe you, big time.

    Thursday, July 7, 2011 1:51 AM
  • Your welcome nTek, I like these sorts of questions. Think the solution will go a long way. There was talk of the application downloading the 'feeds' and that feature can still go in without needing to change very much of what's there. A change in design would be minimal. You can see that the code at the moment works only with FileInfo objects so it's geared towards files only, don't sweat it, but for downloads that can be improved and well actually I was going to leave it but might as well tell you about that now....

    The idea would be to add the concept of the reader reading from a 'data source' rather than a file.  Here's the idea in code.

                IDataSource source = new UriDataSource("http://www.feeds.com/data.rss");
                FileInfo destinationFile = new FileInfo("destination_file.xml");

                AdvertismentReader reader = new AdvertismentReader(source);
                AdvertismentWriter writer = new AdvertismentWriter(destinationFile);

                IEnumerable<Advertisment> advertisments = reader.Read();
                writer.Write(advertisments);

    Interface of IDataSource used instead of just FileInfo.. Simple interface with a GetData method....

    namespace FileConverter.DataSources
    {
        internal interface IDataSource
        {
            string GetData();
        }
    }

    Here is an example of a, not all that good, UriDataSource

    namespace FileConverter.DataSources
    {
        using System.Net;

        internal class UriDataSource : IDataSource
        {
            private readonly string uri;

            public UriDataSource(string uri)
            {
                this.uri = uri;
            }

            public string GetData()
            {
                WebClient web = new WebClient();
                return web.DownloadString(uri);
            }
        }
    }

    and one for a file based data source....

    namespace FileConverter.DataSources
    {
        using System.IO;

        class FileDataSource : IDataSource
        {
            private readonly string path;

            public FileDataSource(string path)
            {
                this.path = path;
            }

            public string GetData()
            {
                return File.ReadAllText(path);
            }
        }
    }

     

    The reader then uses the interface to get the data at the data source and gets the parser for it as normal...

    namespace FileConverter.IO
    {
        using System.Collections.Generic;
        using DataSources;
        using Parsers;

        internal class AdvertismentReader
        {
            private readonly string contents;

            public AdvertismentReader(IDataSource source)
            {
                contents = source.GetData();
            }

            public IEnumerable<Advertisment> Read()
            {
                IParser parser = ParserFactory.Get(this.contents);
                return parser.Parse(this.contents);
            }
        }
    }

     

    The code in the parser factory now only works exclusively with the files content... but that is only in this examlpe, the DataSource could give clues as to what parser is needed... not sure about the last bit there but the idea was to show you how the design can be updated to get data from other sources rather than just files.  Keep the parsers and data sources seperate, don't for example have a parser per data source. Don't use one to determne the other although you could use clues from one to determine the other, if that makes sense.

     

    Used to have a blog and a wiki and used to spend a great deal of personnal time and effort investigating things and posting about them. Used to have my face in code 24/7. Then something very sad happened to someone I know who was just ike me and now I don't do it anymore.  That's why I didn't take you up on your offer.  Would have done before but now my personnal time is not software time. I digress.

     

    No worries at all about the help. Anything else I can help you with come find me on the forum.  take it easy.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    • Marked as answer by ntek designs Thursday, July 7, 2011 1:53 PM
    Thursday, July 7, 2011 9:24 AM
  • Thanks for that Derek!  That's definently something Im going to look into in the near future.. For now im just going to concentrate on getting the Parsers working and move from there.

    And sorry to hear about your friend :( 

     

    Friday, July 8, 2011 1:30 PM
  • Derek,

    Just wanted to check with you before I got going to far,  should I be using another Factory to return the right Datasource?


    Thanks again man.. Your help is much appreciated.

    -nTek

    Friday, July 8, 2011 2:03 PM
  • Hey nTek,

    That's an idea, the factory pattern could be used to return the correct data source... yeah that would work. The other thing that came to mind was no factory classes are actually needed at all; because you could create a manifest file that describes the individual data sources of the application and how they map to parsers. Maybe a factory could take that file and....

    You can see there are loads of ways to code it and it can carry on forever if the situation is over analysed.

     

    Your doing the right thing, concentrate on reading from files and getting the parsers built. When the application moves forward to downloading then you can consider whether a factory would work or having a data source to parser map file or even something else ... it's just good to know that the applications design can adapt to future requirements if they are needed. See what happens.


    "The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination." - Fred Brooks
    Monday, July 11, 2011 8:08 AM