mercredi 14 août 2019

Step Builder for hydrating flat data into complex objects with inheritance

I am trying to utilize a Step Builder class that is able to build a complex object as I loop through a file's data. The data is hierarchical and I will need to nest the data into the object tree depending on the line's prefix.

For example

01,071000288,071000288,190718,0701,1,80,,2/ --file header
02,071000288,071000288,1,190717,,USD,2/ --group header
03,434443,USD,010,131795606,,,015,131772680,,, -- account detail
04,535454,4534,4 -- group trailer
02,071000288,071000288,1,190717,,USD,2/ -- group header
03,43434,USD,010,13179564406,,,015,131772680,,, --account detail
03,76765,USD,010,1317956543506,,,015,131772680,,, --account detail
03,6566,USD,010,131344795606,,,015,131772680,,, --account detail
04,535454,4534,4 - group trailer
08,3443243,23432,433 --file trailer

For each line item I have a class

abstract class FileLineItemBase
{
   public int TypeId { get; set; }
}

class FileHeader : FileLineItemBase {...}
class GroupHeader: FileLineItemBase { ... }
class AccountDetail : FileLineItemBase { ... }
class GroupTrailer: FileLineItemBase { ... }
class FileTrailer: FileLineItemBase { ... }

class File {
   public FileHeader {get; set;}
   public FileTrailer {get; set;}
   public List<Group> Groups {get; set;}
}

class Group{
   public GroupHeader {get; set;}
   public GroupTrailer {get; set;}
   public List<Account> Accounts {get; set;}
}

class Account {
   public AccountDetail {get; set;}
}


I thought a step builder would be perfect for something like this but when I start writing the recursive method that should be reading and calling the builder, I see it is a lot more difficult that I originally thought, is a builder the correct pattern for this problem?

public class Builder
    {
        public static IFileHeaderStep CreateNewBuilder()
        {
            return new Steps();
        }
        private Builder() { }

        public interface IFileHeaderStep {
            IGroupHeaderStep SetFileHeader(string fileHeaderData);
        }
        public interface IGroupHeaderStep {
            IAccountDetailStep SetGroupHeader(string groupHeaderData);
        }
        public interface IAccountDetailStep {
            IGroupTrailerStep SetAccountDetail(string accountDetailData);
        }
        public interface IGroupTrailerStep {
            IFileTrailerStep SetLastGroupTrailer(string groupTrailerData);
            IGroupHeaderStep SetGroupTrailer(string groupHeaderData);
        }
        public interface IFileTrailerStep {
            IBuildStep SetFileTrailer(string fileTrailerData);
        }

        public interface IBuildStep
        {
            RawFile Build();
        }

        private class Steps : IFileHeaderStep, IGroupHeaderStep, IAccountDetailStep, IGroupTrailerStep, IFileTrailerStep, IBuildStep
        {
            public IGroupHeaderStep SetFileHeader(string fileHeaderData) {}
            public IAccountDetailStep SetGroupHeader(string groupHeaderData) {}
            public IGroupTrailerStep SetAccountDetail(string accountDetailData) {}
            public IFileTrailerStep SetLastGroupTrailer(string groupTrailerData) {}
            IGroupHeaderStep IGroupTrailerStep.SetGroupTrailer(string groupHeaderData) {}
            public IBuildStep SetFileTrailer(string fileTrailerData) {}
            public RawFile Build() {}
        }
    }

     public class Testing
    {

        public void Runner()
        {
            var builder = Builder.CreateNewBuilder();
            var allLines = File.ReadAllLines("somepath");

            var file = ReadLines(allLines, 0, builder).Build();
        }
        public Builder ReadLines(string[] lines, int index, Builder builder)
        {
            var line = lines[index];
            var type = line.Substring(0, 2);

            switch (type)
            {
                case Constants.RecordCode.FileHeader:
                    builder.SetFileHeader(ReadLines(lines, ++index, builder));
                        return builder;
                case Constants.RecordCode.GroupHeader:
                    builder.SetGroupHeader(ReadLines(lines, ++index, builder));
                    return builder;
                case Constants.RecordCode.AccountDetail:
                   //...
                default:


            }
        }
    }

Aucun commentaire:

Enregistrer un commentaire