Proper separation of behaviour and data structure when the data structure is complex?












0












$begingroup$


The code in its full glory, complete with unit tests is here: https://github.com/edemo/PDEngine/pull/277/files
(And also some discussion of the issue)



I am struggling with how the principle of separating data structures and behaviour applies here.



This would be the data structure, only with public fields:



package org.rulez.demokracia.pdengine;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.ElementCollection;

public class BeatTableEntity {

@ElementCollection
public Map<Choice, HashMap<Choice, Integer>> table;

public BeatTableEntity() {
super();
}

}


And this would be the behaviour, no data fields:



package org.rulez.demokracia.pdengine;

import java.util.HashMap;

import org.rulez.demokracia.pdengine.exception.IsNullException;

public class BeatTable extends BeatTableEntity {
public enum Direction {
DIRECTION_FORWARD, DIRECTION_BACKWARD
}

public BeatTable() {
table = new HashMap<>();
}

public int beatInformation(Choice choice1, Choice choice2, Direction direction) throws IsNullException {
if(direction == null)
throw new IllegalArgumentException("Null direction");

int result = 0;

if(direction.equals(Direction.DIRECTION_FORWARD))
result = getBeatValue(choice1, choice2);
else
result = getBeatValue(choice2, choice1);

return result;
}

private HashMap<Choice, Integer> getBeatRow(Choice choice) {
if (table.get(choice) == null)
throw new IllegalArgumentException("Not existing row key");
return table.get(choice);
}

private int getBeatValue(Choice choiceF, Choice choiceS) throws IsNullException {
if (choiceF == null || choiceS == null)
throw new IsNullException("Choice");
HashMap<Choice, Integer> row = getBeatRow(choiceF);
if (row.get(choiceS) == null)
throw new IllegalArgumentException("Not existing column key");
return row.get(choiceS);
}

}


This is obviously badly encapsulated; an example is the test which changes a whole row to an empty one, and calls beatInformation() to throw an exception.
That test fiddles around with the internals of the data representation (which should have no internals in the first place), and results in an error which is not exactly making sense in the business logic level.



My question is: what would be the right data structure to back this behaviour.



We came up with two possible solutions, none of them could really convince me:




  1. have table as a simple map , where key is effectively the (Choice choice1, Choice choice2) tuple (e.g. choice1.id + choice2.id, as those are strings). This way the data structure have no internals to hide, but it is slow for a non-sparse matrix, and I do believe that good design is not slow.

  2. come up with a Matrix class which we think of as a "primitive type". Matrix would itself use an internal data structure fully encapsulated, and implement its own serialization. The reasoning of this would be that this complexity of data cannot work without internals, and as the compiler can hide the internals of e.g. a HashMap, we just do the same, and thus we can use Matrix as a building block in properly separated code.

  3. ???


What are the points to consider here, and what would be the Right Solution(tm) ?










share|improve this question







New contributor




Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$

















    0












    $begingroup$


    The code in its full glory, complete with unit tests is here: https://github.com/edemo/PDEngine/pull/277/files
    (And also some discussion of the issue)



    I am struggling with how the principle of separating data structures and behaviour applies here.



    This would be the data structure, only with public fields:



    package org.rulez.demokracia.pdengine;

    import java.util.HashMap;
    import java.util.Map;

    import javax.persistence.ElementCollection;

    public class BeatTableEntity {

    @ElementCollection
    public Map<Choice, HashMap<Choice, Integer>> table;

    public BeatTableEntity() {
    super();
    }

    }


    And this would be the behaviour, no data fields:



    package org.rulez.demokracia.pdengine;

    import java.util.HashMap;

    import org.rulez.demokracia.pdengine.exception.IsNullException;

    public class BeatTable extends BeatTableEntity {
    public enum Direction {
    DIRECTION_FORWARD, DIRECTION_BACKWARD
    }

    public BeatTable() {
    table = new HashMap<>();
    }

    public int beatInformation(Choice choice1, Choice choice2, Direction direction) throws IsNullException {
    if(direction == null)
    throw new IllegalArgumentException("Null direction");

    int result = 0;

    if(direction.equals(Direction.DIRECTION_FORWARD))
    result = getBeatValue(choice1, choice2);
    else
    result = getBeatValue(choice2, choice1);

    return result;
    }

    private HashMap<Choice, Integer> getBeatRow(Choice choice) {
    if (table.get(choice) == null)
    throw new IllegalArgumentException("Not existing row key");
    return table.get(choice);
    }

    private int getBeatValue(Choice choiceF, Choice choiceS) throws IsNullException {
    if (choiceF == null || choiceS == null)
    throw new IsNullException("Choice");
    HashMap<Choice, Integer> row = getBeatRow(choiceF);
    if (row.get(choiceS) == null)
    throw new IllegalArgumentException("Not existing column key");
    return row.get(choiceS);
    }

    }


    This is obviously badly encapsulated; an example is the test which changes a whole row to an empty one, and calls beatInformation() to throw an exception.
    That test fiddles around with the internals of the data representation (which should have no internals in the first place), and results in an error which is not exactly making sense in the business logic level.



    My question is: what would be the right data structure to back this behaviour.



    We came up with two possible solutions, none of them could really convince me:




    1. have table as a simple map , where key is effectively the (Choice choice1, Choice choice2) tuple (e.g. choice1.id + choice2.id, as those are strings). This way the data structure have no internals to hide, but it is slow for a non-sparse matrix, and I do believe that good design is not slow.

    2. come up with a Matrix class which we think of as a "primitive type". Matrix would itself use an internal data structure fully encapsulated, and implement its own serialization. The reasoning of this would be that this complexity of data cannot work without internals, and as the compiler can hide the internals of e.g. a HashMap, we just do the same, and thus we can use Matrix as a building block in properly separated code.

    3. ???


    What are the points to consider here, and what would be the Right Solution(tm) ?










    share|improve this question







    New contributor




    Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.







    $endgroup$















      0












      0








      0





      $begingroup$


      The code in its full glory, complete with unit tests is here: https://github.com/edemo/PDEngine/pull/277/files
      (And also some discussion of the issue)



      I am struggling with how the principle of separating data structures and behaviour applies here.



      This would be the data structure, only with public fields:



      package org.rulez.demokracia.pdengine;

      import java.util.HashMap;
      import java.util.Map;

      import javax.persistence.ElementCollection;

      public class BeatTableEntity {

      @ElementCollection
      public Map<Choice, HashMap<Choice, Integer>> table;

      public BeatTableEntity() {
      super();
      }

      }


      And this would be the behaviour, no data fields:



      package org.rulez.demokracia.pdengine;

      import java.util.HashMap;

      import org.rulez.demokracia.pdengine.exception.IsNullException;

      public class BeatTable extends BeatTableEntity {
      public enum Direction {
      DIRECTION_FORWARD, DIRECTION_BACKWARD
      }

      public BeatTable() {
      table = new HashMap<>();
      }

      public int beatInformation(Choice choice1, Choice choice2, Direction direction) throws IsNullException {
      if(direction == null)
      throw new IllegalArgumentException("Null direction");

      int result = 0;

      if(direction.equals(Direction.DIRECTION_FORWARD))
      result = getBeatValue(choice1, choice2);
      else
      result = getBeatValue(choice2, choice1);

      return result;
      }

      private HashMap<Choice, Integer> getBeatRow(Choice choice) {
      if (table.get(choice) == null)
      throw new IllegalArgumentException("Not existing row key");
      return table.get(choice);
      }

      private int getBeatValue(Choice choiceF, Choice choiceS) throws IsNullException {
      if (choiceF == null || choiceS == null)
      throw new IsNullException("Choice");
      HashMap<Choice, Integer> row = getBeatRow(choiceF);
      if (row.get(choiceS) == null)
      throw new IllegalArgumentException("Not existing column key");
      return row.get(choiceS);
      }

      }


      This is obviously badly encapsulated; an example is the test which changes a whole row to an empty one, and calls beatInformation() to throw an exception.
      That test fiddles around with the internals of the data representation (which should have no internals in the first place), and results in an error which is not exactly making sense in the business logic level.



      My question is: what would be the right data structure to back this behaviour.



      We came up with two possible solutions, none of them could really convince me:




      1. have table as a simple map , where key is effectively the (Choice choice1, Choice choice2) tuple (e.g. choice1.id + choice2.id, as those are strings). This way the data structure have no internals to hide, but it is slow for a non-sparse matrix, and I do believe that good design is not slow.

      2. come up with a Matrix class which we think of as a "primitive type". Matrix would itself use an internal data structure fully encapsulated, and implement its own serialization. The reasoning of this would be that this complexity of data cannot work without internals, and as the compiler can hide the internals of e.g. a HashMap, we just do the same, and thus we can use Matrix as a building block in properly separated code.

      3. ???


      What are the points to consider here, and what would be the Right Solution(tm) ?










      share|improve this question







      New contributor




      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.







      $endgroup$




      The code in its full glory, complete with unit tests is here: https://github.com/edemo/PDEngine/pull/277/files
      (And also some discussion of the issue)



      I am struggling with how the principle of separating data structures and behaviour applies here.



      This would be the data structure, only with public fields:



      package org.rulez.demokracia.pdengine;

      import java.util.HashMap;
      import java.util.Map;

      import javax.persistence.ElementCollection;

      public class BeatTableEntity {

      @ElementCollection
      public Map<Choice, HashMap<Choice, Integer>> table;

      public BeatTableEntity() {
      super();
      }

      }


      And this would be the behaviour, no data fields:



      package org.rulez.demokracia.pdengine;

      import java.util.HashMap;

      import org.rulez.demokracia.pdengine.exception.IsNullException;

      public class BeatTable extends BeatTableEntity {
      public enum Direction {
      DIRECTION_FORWARD, DIRECTION_BACKWARD
      }

      public BeatTable() {
      table = new HashMap<>();
      }

      public int beatInformation(Choice choice1, Choice choice2, Direction direction) throws IsNullException {
      if(direction == null)
      throw new IllegalArgumentException("Null direction");

      int result = 0;

      if(direction.equals(Direction.DIRECTION_FORWARD))
      result = getBeatValue(choice1, choice2);
      else
      result = getBeatValue(choice2, choice1);

      return result;
      }

      private HashMap<Choice, Integer> getBeatRow(Choice choice) {
      if (table.get(choice) == null)
      throw new IllegalArgumentException("Not existing row key");
      return table.get(choice);
      }

      private int getBeatValue(Choice choiceF, Choice choiceS) throws IsNullException {
      if (choiceF == null || choiceS == null)
      throw new IsNullException("Choice");
      HashMap<Choice, Integer> row = getBeatRow(choiceF);
      if (row.get(choiceS) == null)
      throw new IllegalArgumentException("Not existing column key");
      return row.get(choiceS);
      }

      }


      This is obviously badly encapsulated; an example is the test which changes a whole row to an empty one, and calls beatInformation() to throw an exception.
      That test fiddles around with the internals of the data representation (which should have no internals in the first place), and results in an error which is not exactly making sense in the business logic level.



      My question is: what would be the right data structure to back this behaviour.



      We came up with two possible solutions, none of them could really convince me:




      1. have table as a simple map , where key is effectively the (Choice choice1, Choice choice2) tuple (e.g. choice1.id + choice2.id, as those are strings). This way the data structure have no internals to hide, but it is slow for a non-sparse matrix, and I do believe that good design is not slow.

      2. come up with a Matrix class which we think of as a "primitive type". Matrix would itself use an internal data structure fully encapsulated, and implement its own serialization. The reasoning of this would be that this complexity of data cannot work without internals, and as the compiler can hide the internals of e.g. a HashMap, we just do the same, and thus we can use Matrix as a building block in properly separated code.

      3. ???


      What are the points to consider here, and what would be the Right Solution(tm) ?







      design-patterns






      share|improve this question







      New contributor




      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question







      New contributor




      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question






      New contributor




      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 2 hours ago









      Árpád MagosányiÁrpád Magosányi

      101




      101




      New contributor




      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      Árpád Magosányi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          Árpád Magosányi is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215960%2fproper-separation-of-behaviour-and-data-structure-when-the-data-structure-is-com%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          Árpád Magosányi is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          Árpád Magosányi is a new contributor. Be nice, and check out our Code of Conduct.













          Árpád Magosányi is a new contributor. Be nice, and check out our Code of Conduct.












          Árpád Magosányi is a new contributor. Be nice, and check out our Code of Conduct.
















          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215960%2fproper-separation-of-behaviour-and-data-structure-when-the-data-structure-is-com%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Terni

          A new problem with tex4ht and tikz

          Sun Ra