Skip to main content
deleted 14 characters in body
Source Link
John Wu
  • 26.9k
  • 10
  • 68
  • 92

I'm going to introduce a few programming concepts here so bear with the long answer. This may seem overcomplicated butuntil you're used to it. But all of these patterns willare actually bevery common and very useful in commercial software.

public class DocumentParser : BaseParser
{
    protected override List<Parser> GetParsers()
    {
        return new List<Parser>
        {
            ParseDateOfBirth,
            ParseGender,
            ParseRefNum
        };
    }
    
    protectedprivate bool ParseDateOfBirth(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("DOB:")) return false;
        dto.DateOfBirth = DateTime.Parse(line.Substring(4));
        return true;///Implementation
    }
    
    protectedprivate bool ParseGender(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("Sex:")) return false;
        var s = line.Substring(4,1);
        if (s == "M")
        {
            dto.Gender = Gender.Male;
            return true;
        }
        if (s == "F")
        {
            dto.Gender = Gender.Female;
            return true;
        }
 ///Implementation       return false;
    }
    
    protected virtualprivate bool ParseRefNum(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("RefNum:")) return false;
        
        dto.RefNum = line.Substring(7);
        return true;///Implementation
    }
}

I'm going to introduce a few programming concepts here so bear with the long answer. This may seem overcomplicated but all of these patterns will actually be very useful in commercial software.

public class DocumentParser : BaseParser
{
    protected override List<Parser> GetParsers()
    {
        return new List<Parser>
        {
            ParseDateOfBirth,
            ParseGender,
            ParseRefNum
        };
    }
    
    protected bool ParseDateOfBirth(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("DOB:")) return false;
        dto.DateOfBirth = DateTime.Parse(line.Substring(4));
        return true;
    }
    
    protected bool ParseGender(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("Sex:")) return false;
        var s = line.Substring(4,1);
        if (s == "M")
        {
            dto.Gender = Gender.Male;
            return true;
        }
        if (s == "F")
        {
            dto.Gender = Gender.Female;
            return true;
        }
        return false;
    }
    
    protected virtual bool ParseRefNum(string line, DocumentMetadata dto)
    {
        if (!line.StartsWith("RefNum:")) return false;
        
        dto.RefNum = line.Substring(7);
        return true;
    }
}

I'm going to introduce a few programming concepts here so bear with the long answer. This may seem overcomplicated until you're used to it. But all of these patterns are actually very common and very useful in commercial software.

public class DocumentParser : BaseParser
{
    protected override List<Parser> GetParsers()
    {
        return new List<Parser>
        {
            ParseDateOfBirth,
            ParseGender,
            ParseRefNum
        };
    }
    
    private bool ParseDateOfBirth(string line, DocumentMetadata dto)
    {
       ///Implementation
    }
    
    private bool ParseGender(string line, DocumentMetadata dto)
    {
       ///Implementation        
    }
    
    private bool ParseRefNum(string line, DocumentMetadata dto)
    {
       ///Implementation
    }
}
added 250 characters in body
Source Link
John Wu
  • 26.9k
  • 10
  • 68
  • 92
public enum Gender
{
    Male, Female
}
 

public class DocumentMetadata
{
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
    public string RefNum { get; set; }
    
    public override string ToString()
    {
        return string.Format("DocumentMetadata: DOB={0:yyyy-MM-dd}, Gender={1}, RefNum={2}", DateOfBirth, Gender, RefNum);
    }
}
protected bool ParseDateOfBirth(string line, DocumentMetadata dto)
{
    if (!line.StartsWith("DOB:")) return false;
    dto.DateOfBirth = DateTime.Parse(line.Substring(4));
    return true;
}

We can write any number of parser methods, and as long as they return a boolean and accept a string and a DTO object as arguments, they will all match the delegate, so they could all be put into a list, sort of like this:

List<Parser>  parsers = new List<Parser>
{
    ParseDateOfBirth,
    ParseGender,
    ParseRefNum
};

We'll use this capability in a moment when we write the parser class.

public abstract class BaseParser : IDocumentParser
{
    protected abstract List<Parser> GetParsers();
    
    public virtual DocumentMetadata Parse(IEnumerable<string> input)
    {
        var parsers = this.GetParsers();
        var instance = new DocumentMetadata();
        
        foreach (var line in input)
        {
            foreach (var parser in parsers)
            {
                parser(line, instance);  //This is the line that does it all!!!
            }
        }
        return instance;
    }       
}

While the LINQ solution is more "clever" the nested loops may communicate the intent more clearly. Judgment call here. I like the LINQ version because I can count the lines that succeed, and possibly use that information to validate the document.

public enum Gender
{
    Male, Female
}
 

public class DocumentMetadata
{
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
    public string RefNum { get; set; }
    
    public override string ToString()
    {
        return string.Format("DocumentMetadata: DOB={0:yyyy-MM-dd}, Gender={1}, RefNum={2}", DateOfBirth, Gender, RefNum);
    }
}
protected bool ParseDateOfBirth(string line, DocumentMetadata dto)
{
    if (!line.StartsWith("DOB:")) return false;
    dto.DateOfBirth = DateTime.Parse(line.Substring(4));
    return true;
}
public abstract class BaseParser : IDocumentParser
{
    protected abstract List<Parser> GetParsers();
    
    public virtual DocumentMetadata Parse(IEnumerable<string> input)
    {
        var parsers = this.GetParsers();
        var instance = new DocumentMetadata();
        
        foreach (var line in input)
        {
            foreach (var parser in parsers)
            {
                parser(line, instance);
            }
        }
        return instance;
    }       
}
public enum Gender
{
    Male, Female
}

public class DocumentMetadata
{
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
    public string RefNum { get; set; }
    
    public override string ToString()
    {
        return string.Format("DocumentMetadata: DOB={0:yyyy-MM-dd}, Gender={1}, RefNum={2}", DateOfBirth, Gender, RefNum);
    }
}
protected bool ParseDateOfBirth(string line, DocumentMetadata dto)
{
    if (!line.StartsWith("DOB:")) return false;
    dto.DateOfBirth = DateTime.Parse(line.Substring(4));
    return true;
}

We can write any number of parser methods, and as long as they return a boolean and accept a string and a DTO object as arguments, they will all match the delegate, so they could all be put into a list, sort of like this:

List<Parser>  parsers = new List<Parser>
{
    ParseDateOfBirth,
    ParseGender,
    ParseRefNum
};

We'll use this capability in a moment when we write the parser class.

public abstract class BaseParser : IDocumentParser
{
    protected abstract List<Parser> GetParsers();
    
    public virtual DocumentMetadata Parse(IEnumerable<string> input)
    {
        var parsers = this.GetParsers();
        var instance = new DocumentMetadata();
        
        foreach (var line in input)
        {
            foreach (var parser in parsers)
            {
                parser(line, instance);  //This is the line that does it all!!!
            }
        }
        return instance;
    }       
}

While the LINQ solution is more "clever" the nested loops may communicate the intent more clearly. Judgment call here. I like the LINQ version because I can count the lines that succeed, and possibly use that information to validate the document.

added 428 characters in body
Source Link
John Wu
  • 26.9k
  • 10
  • 68
  • 92

Use a list of delegates

Use a list of delegates

...to call idemopotent parsing functions encapsulated in an injectable class contained in an extesnsible object model

I'm going to introduce a few programming concepts here so bear with the long answer. This may seem overcomplicated but all of these patterns will actually be very useful in commercial software.

Create a DTO

Define a delegate for parser methods that follow a pattern

Create the base parser class

Implement the parsers

Test

Now we have all of the following:

  1. Generic logic for iterating over all the parsers
  2. An extensible object model allowing new parsers to be introduced
  3. An injectable interface
  4. Idemopotent, unit-testable methods that do the complicated stuff
  5. The ability to count successful parse operations (which could be used, for example, to ensure the document is valid)
  6. A class that encapsulates the resulting data

Use a list of delegates

I'm going to introduce a few programming concepts here so bear with the long answer.

Use a list of delegates

...to call idemopotent parsing functions encapsulated in an injectable class contained in an extesnsible object model

I'm going to introduce a few programming concepts here so bear with the long answer. This may seem overcomplicated but all of these patterns will actually be very useful in commercial software.

Create a DTO

Define a delegate for parser methods that follow a pattern

Create the base parser class

Implement the parsers

Test

Now we have all of the following:

  1. Generic logic for iterating over all the parsers
  2. An extensible object model allowing new parsers to be introduced
  3. An injectable interface
  4. Idemopotent, unit-testable methods that do the complicated stuff
  5. The ability to count successful parse operations (which could be used, for example, to ensure the document is valid)
  6. A class that encapsulates the resulting data
Source Link
John Wu
  • 26.9k
  • 10
  • 68
  • 92
Loading