Proper separation of behaviour and data structure when the data structure is complex?
$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:
- 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.
- 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.
- ???
What are the points to consider here, and what would be the Right Solution(tm) ?
design-patterns
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$
add a comment |
$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:
- 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.
- 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.
- ???
What are the points to consider here, and what would be the Right Solution(tm) ?
design-patterns
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$
add a comment |
$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:
- 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.
- 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.
- ???
What are the points to consider here, and what would be the Right Solution(tm) ?
design-patterns
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:
- 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.
- 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.
- ???
What are the points to consider here, and what would be the Right Solution(tm) ?
design-patterns
design-patterns
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.
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.
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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.
Á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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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