3

Let say I own a resturant that makes a pie. I want to cut this pie according to the input parsed from the file. That hold the key to the information on how the Pie is to be cut. I use CircleDividerParser class to parse the file.txt.

file format:

  • pie_cuttin_style_name = is key for searching it back
  • radius = radius or pie
  • x = x-coordinate of pie center
  • y = y-coordinate of pie center
  • angle_type = angle can be either degree or radian
  • angle_value = value of angle that could be radian or degree
  • direction = clock wise or anti-clockwise angle
  • from_x = x point in circle from where the angle is measured
  • from_y = y point in circle from where the angle is
    measured

eg: file.txt

cutting_pattern_1,5,1,2,degree,40,anti-clock,6,2

....

..

Then I have class for doing the parsing logic:

Class CircleDividerParser{

  // does the parsing logic to capture the values
  void Dowork(filename){
    std::ifstream file_stream.open(filename);
    std::string line;
    while(std::getline(file_stream, line){
       std::string str;
       std::stringstream ss(line);
       int i = 0;
       while(std::getline(ss, str, ',')){
         holder[i] = str; 
         i++;
       }
       pcc.add_pie_cutting_info(holder);
    }

  }

  private:
   std::array<std::string, 9> holder;
   PieCutterCreator pcc;

};

// This class does the creation of PieCuttingInfo based on the array of string provided from CicleDividerParser

class PieCutterCreator{

public:
  void add_pie_cutting_info(std::array<std::string, 9> arr){
     PieCuttingInfo obj;    

     obj.AddPieCuttingStyleName(arr[0]);
     obj.AddRadius(arr[1]);
     ...
     ..

  }
  PieCuttingInfo GetPieCuttingInfo(const std::string& name_to_find);

  std::vector<PieCuttingInfo> GetAllPieCuttingInfo() { return collect;}

private:
  std::vector<PieCuttingInfo> collect;

};

class PieCuttingInfo{

  class enum angle_type {Degree, Radian};

public:
  PieCuttingInfo()=default;

  void AddPieCuttingStyleName(const std::string& name){ pie_cutting_style_name = name;}

  void AddRaius(const std::string& r) {radius = r;}

  void AddAngleType(const std::string& at) {
    if(at == "degree"){
      angle_value = angle_type::Degree;
    }else if(at == "radian"){
      angle_value = angle_type::Radian;
    }else {// error}
  }
   ...


private:

 std::string pie_cutting_style_name;
 std::string radius;// radius or pie 
 std::string x;//  x-coordinate of pie center
 std::string y;//  y-coordinate of pie center
 std::string angle_type;  angle can be either degree or radian
 std::string angle_value; value of angle that could be radian or degree
 std::string direction;  clock wise or anti-clockwise angle
 std::string from_x;   x point in circle from where the angle is measured
 std::string from_y;   y point in circle from where the angle is measured

};


class PieCuttingMaching(){

  void DoAllThePieCutting(){
    save = obj.GetAllPieCuttingInfo();
    for(auto& it: save){
        // use the info from PieCuttingInfo to do the job
    }
  }  

private:
  std::vector<PieCuttingInfo> save;
  PieCutterCreator obj;
}

Questions :

  • Is the design I am following correct or am I making things compilcated
  • Can a class PieCuttingInfo be created even if all the information is not provided.
  • Should the variables the PieCuttingInfo class be initialized during its creation by passing all the variable at once.
3

Well, what you see before you is really a breakfast food cooker. Joking aside, it sounds like your solution is allowing room for mistakes in your read file, and your program would have trouble determining potential mistakes of this nature. I can't say because perhaps the machine slicing the pies need all this information and you want to give maximum control to the person writing the file.

There's nothing wrong with this per se, but it's very low level. That is to say, the person writing the parameters has to get everything right, not you. If this were input to a machine, there is serious possibility of making the machine do things that it can't do or attempts to do and ends up destroying itself in the process.

Simplification is key

These may be the actual parameters of the machine, but my advice to you would be to simplify pie cutting operations into use cases, and translate the "simple" parameters into the more complex ones that your machine can execute. Start with an abstract CuttingInfo class with the method GetMachineParams. Any class deriving from CuttingInfo takes as input simplified parameters and its output to GetMachineParams are the low level details to pass to the machine.

You can likewise create an abstract Parser class with methods CanRead and Read. CanRead can be called with the current line of the file to determine if it can be deserialized by that class, and Read is consequently called when CanRead returns true which returns an instance of CuttingInfo.

Use Case - 8 slices

I want the machine to divide my pie into 8 even slices. This is going to be a popular request no doubt. If not 8 slices, then 10 or 6, but nonetheless the user of your program aka your client, isn't going to talk about the number of radians per slice or moving counterclockwise.

Class NEvenSlicesCuttingInfo takes as a parameter only a number presenting N. The actual work of translating this into a series of instructions, goes into GetMachineParams.

You then only need a Class NEvenSlicesParser which returns true on CanRead if and only if the line starts with "NEvenSlices". The second parameter on the same line must be N. So the line in the file should be:

NEvenSlices 8

The job of NEvenSlicesParser in Read should be nothing more sophisticated than reading the parameter after NEvenSlices and returning a new NEvenSlicesCuttingInfo(numberRead).

Granted, the file could still contain improper parameters in this way, but you can check the simplified parameters. Assuming your class does as it should, you don't need to worry about breaking the machine with these parameters.

Bringing it together

To use it, in your program you would have a vector of Parser classes instantiated once on program start with all implementations of Parser. As you read the file, line by line, you pass to CanRead. If it returns true, call its Read method and receive a CuttingInfo that you can call at any time to retrieve machine parameters. All of this without knowing the implementation details in each implementation class.

You can see clearly you can expand on this to add other use cases. You can even cover your original scenario by creating a 1 to 1 parser that loads every possible parameter and simply returns it in GetMachineParams should you require absolute control that doesn't break this pattern if you require it (though I would strongly discourage the use of such a class).

This approach has the disadvantage that you would have to rewrite the instruction file to contain all information on the same line, but nothing's keeping you from creating a more sophisticated Parser which has some sort of convention for writing instructions on multiple lines (i.e. if you see line start with <<<, read until you find line ending with >>> and return contents in the middle to Parser.CanRead).

The crucial thing is that your program logic no longer cares about implementation details, allowing you maximum flexibility and easy future maintenance.

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.