0

I want to understand different ways to object design classes. I have three different classes. Generally I am creating a file parser.

The classes:

  1. CustomerData - which shows text Data Model.
  2. A File Parser, which will take data from file and place into Generic List
  3. And Folder reader, which will run FileParse for All Files in a Directory

Should I separate everything into different classes, combine class 1 and 2, or have everything in one united class per below. What is the software architect method? If there is no One answer, what business requirements or principles should I use to make this decision? I would think 'Single Responsibility Principle' states they should be in own class, or is that only for Functions?

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;

    namespace ParseTest
    {
    public class Customer
    {

        public class CustomerData
        {
            // These are the column names in customerdata txt:
            public int CustomerId { get; set; }
            public string CustomerName { get; set; }
            public string CustomerState { get; set; }
            public int ProductId { get; set; }
            public int QuantityBought { get; set; }
        }

        public List<CustomerData> GetCustomer(string filename)
        {
            List<CustomerData> customerdata = new List<CustomerData>();
            //const String CustomerBase = @"C:\Users\Desktop\ParseFile\sample.txt";

            string CustomerBase = filename;

            String fileToLoad = String.Format(CustomerBase);
            using (StreamReader r = new StreamReader(fileToLoad))
            {
                string line;
                while ((line = r.ReadLine()) != null)
                {
                    string[] parts = line.Split(',');
                    // Skip the column names row
                    if (parts[0] == "id") continue;
                    CustomerData dbp = new CustomerData();
                    dbp.CustomerId = Convert.ToInt32(parts[0]);
                    dbp.CustomerName = parts[1];
                    dbp.CustomerState = parts[2];
                    dbp.ProductId = Convert.ToInt32(parts[3]);
                    dbp.QuantityBought = Convert.ToInt32(parts[4]);
                    customerdata.Add(dbp);
                }
            }
            return customerdata;
        }

        public List<CustomerData> GetAllCustomer(string directoryname)
        {

            List<CustomerData> AllFileCustomerData = new List<CustomerData>();

            foreach (string filename in Directory.EnumerateFiles(directoryname, "*.txt"))
            {
                List<CustomerData> customerdata = new List<CustomerData>();
                customerdata = GetCustomer(filename);
                AllFileCustomerData.AddRange(customerdata);

            }
            return AllFileCustomerData;
        }

    }
}
1

As architecture perspective the ideal solution is to separate your model from other code so you have a class called CustomerModel.cs :

public class CustomerModel
{
    // These are the column names in customerdata txt:
    public int CustomerId { get; set; }
    public string CustomerName { get; set; }
    public string CustomerState { get; set; }
    public int ProductId { get; set; }
    public int QuantityBought { get; set; }
}

Then the best implementation is an interface which let you implement other file parser if needed, IFileParser.cs :

public interface IFileParser
{
    List<T> ParseFiles<T>(string[] filePath);
    List<T> ParseDirectory<T>(string directoryPath);
}

And implement your customer file parser like below, CustomerParser.cs

class CustomerParser : IFileParser
{
    public List<T> ParseFiles<T>(string[] filePath)
    {
        List<CustomerModel> allFileCustomerData = new List<CustomerModel>();

        //string[] customerBase = { @"C:\Users\Ritwik\Desktop\ParseFile\sample.txt"};

        string[] customerBase = filePath;

        foreach (string customerFile in customerBase)
        {
            String fileToLoad = String.Format(customerFile);
            using (StreamReader r = new StreamReader(fileToLoad))
            {
                string line;
                while ((line = r.ReadLine()) != null)
                {
                    string[] parts = line.Split(',');
                    // Skip the column names row
                    if (parts[0] == "id") continue;

                    CustomerModel dbp = new CustomerModel
                    {
                        CustomerId = Convert.ToInt32(parts[0]),
                        CustomerName = parts[1],
                        CustomerState = parts[2],
                        ProductId = Convert.ToInt32(parts[3]),
                        QuantityBought = Convert.ToInt32(parts[4])
                    };

                    allFileCustomerData.Add(dbp);
                }
            }
        }

        return new List<T>(allFileCustomerData as IEnumerable<T> ?? throw new InvalidOperationException());
    }

    public List<T> ParseDirectory<T>(string directoryPath)
    {
        string[] customerFiles = Directory.GetFiles(directoryPath, "*.txt");
        return ParseFiles<T>(customerFiles);
    }

And your solution should be something like :

enter image description here

  • thanks, I get it, thats interesting, you allow the parse file method allow more than one file, originally I had this, GetCustomer(string filename), does this break the single responsibility principle? Thanks- – CarSpeed87 Sep 22 '18 at 7:11
  • also, had question here if you know, stackoverflow.com/questions/52454117/… – CarSpeed87 Sep 22 '18 at 7:12
  • I change the code to implement the IFileParser interface using generics, now you can easily implement other file parser using this interface. And for the question you ask in comment, no it doesn't break the single responsibility. single responsibility states that every module or class should have responsibility over a single part of the functionality provided by the software and our class do it ! it just focus on parsing customer files, multiple customer files does not matter. – Mojtaba Tajik Sep 22 '18 at 7:23
  • you might want to abstract yourself from the filesystem and accept something like a list of IStream instead of filenames, in case your customer comes from somewhere else. Placing another layer in between actual files and the parser, like an additional ICustomerContentProvider injected in the parser class might also work – BgrWorker Oct 22 '18 at 9:03
  • As written, this approach creates interface that could easily be user to try to parse incorrect file types. I would suggest an ICustomerFileParser instead. – Eric Oct 22 '18 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.