Nested Enums with Constructors & Methods? - java

I'm a Java student (relatively new) working on a game clone in Java as a side project. In the game, the player controls characters. Each character is part of a faction, and each faction has a set list of skills. The skills of one faction can not be used by any other faction. My idea for how to organize this is with nested enums, where a main Skills enum has multiple inner enums (Faction1, Faction2, etc). The idea is that I would be able to access the data for any specific skill using something along the lines of Skills.Faction1.SKILL_NAME, and to be able to access a full list of a faction's skills using Skills.Faction1.values(). A simplified example of a failed implementation of this is as follows:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
...
}
enum Faction2 {
FACTION2_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION2_LAST_SKILL("arbitraryArgs");
...
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
void doUniversalThing() {
//Code here
}
}
When I try to use this implementation, I get errors telling me that the constructors and/or fields for the values in the inner enums don't exist. I tried copy pasting the constructors and fields into each individual inner enum, however that was both unreadable and unaccommodating to potential future skills which would not be attached to a faction. This simplified example doesn't even include all of the methods and alternate constructors that must be accessible to each skill. How could I implement this idea effectively and elegantly in a way that both supports skills which are a member of a faction and skills which are not, assuming that I could implement it at all? I tried my best to explain the intended results of the code, but if anything is still unclear then just let me know. Thank you.
Edit: The contents of Faction1 were requested, so in additon to me rewriting my initial code example to maybe give a better idea of my intentions, here's a few different ways I've tried Faction1. All were either erroneous or just not ideal.
Attempt 1:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
}
My first attempt was just this, which gave me an error that The constructor Skills.Faction2(String) is undefined. I understand that this is due to Faction2 being its own class and unable to use a Skills constructor, which is why I then moved to my second attempt.
Attempt 2:
public enum Skills {
FACTIONLESS_SKILL("arbitraryArgs"); //More skills than just this one
enum Faction1 {
FACTION1_FIRST_SKILL("arbitraryArgs"), //More skills between the 2
FACTION1_LAST_SKILL("arbitraryArgs");
String arbitraryField; Duplicates of Skills fields
Faction1(String arbitraryArgs) { //Duplicate of Skills constructor
this.arbitraryField = arbitraryArgs;
}
}
String arbitraryField; //1 of many fields universal among both factionless and factioned skills
Skills(String arbitraryArgs) { //1 of many universal constructors
this.arbitraryField = arbitraryArgs;
}
}
This solution technically works, in that there are no errors. However, my issue with this solution is the insane amount of code duplication this causes in my non-reduced program. Every skill has numerous fields, constructors, and methods, whether the skill is assigned to a faction or not. There are also numerous factions that would need to be made. If I ever realized that a field or method or constructor was either unneeded and should be removed or needed and should be created, I would need to create or remove it from every faction individually. This is just honestly not something I want to do on a silly side project.
I haven't thought of any other way to create these inner enums, nor have I seen any in my research, so these are my only 2 implementations so far. I hope this clears things up a bit.

A brief OOPs class to begin with --
The Faction and Skill entities seem to have a has a relationship i.e., A Factions HAS A set of Skills. Also I will create an additional enum SkillName which again has a HAS A relation ship with Skill. So keeping these in mind you could arrange you enums like so --
public enum Faction {
FACTION1(new HashMap<SkillName, Skill>(){{
put(Skill.SNIPER_SHOT.skillName(), Skill.SNIPER_SHOT);
}}),
FACTION2(new HashMap<SkillName, Skill>(){{
put(Skill.JUDGEMENT.skillName(), Skill.JUDGEMENT);
}});
Map<SkillName, Skill> skills;
Faction(Map<SkillName, Skill> skills) {
this.skills = skills;
}
public Skill[] skills(){
return this.skills.values().toArray(new Skill[0]);
}
public Skill skill(SkillName name){
Skill skill = this.skills.get(name);
if(Objects.isNull(skill)){
throw new IllegalArgumentException("Invalid Skill name");
}
return skill;
}
}
enum SkillName {
SNIPER_SHOT("SNIPER_SHOT"),
JUDGEMENT("JUDGEMENT");
String value;
SkillName(String value){
this.value = value;
}
}
enum Skill {
SNIPER_SHOT(SkillName.SNIPER_SHOT, 0, 95, 5, new boolean[] {true, true, false, false}, new boolean[] {false, true, true, true}),
JUDGEMENT(SkillName.JUDGEMENT,-25, 85, 5, new boolean[] {true, true, false, false}, new boolean[] {true, true, true, true});
SkillName name;
Integer dmg;
Integer acc;
Integer crit;
boolean[] rank;
boolean[] target;
Skill(SkillName name, Integer dmg, Integer acc, Integer crit, boolean[] rank, boolean[] target) {
this.name = name;
this.dmg = dmg;
this.acc = acc;
this.crit = crit;
this.rank = rank;
this.target = target;
}
public SkillName skillName() {
return this.name;
}
}
Now you could access the skills and a specific skill like so, --
Faction.FACTION1.skills();
Faction.FACTION2.skills();
Faction.FACTION1.skill(SkillName.SNIPER_SHOT);
Faction.FACTION1.skill(SkillName.JUDGEMENT); // this should throw exception
Faction.FACTION2.skill(SkillName.SNIPER_SHOT); // this should throw exception
Faction.FACTION2.skill(SkillName.JUDGEMENT);
The access patterns are not exactly the same as you wanted, but these work pretty much to the same extent.
SIDE NOTE -
Not sure what type of game you are developing, but in most of the games, skill of players improve (i.e. skill properties change) and players acquire new skills as well. Hence the above setup will become very rigid as enums are immutable and you will not be able to scale this. Instead, what I would suggest is that you create classes for Factions and Skills instead of enums. This way you would be able to mutate them.

Just decided to upgrade to Java 9 to use Map.of(). Thank you all for your help!

Seems like you already found your solution, but here is something that is probably much easier to use.
import java.util.Arrays;
import java.util.List;
/** Enums. https://stackoverflow.com/questions/71517372 */
public class SOQ_20220406_0201
{
/**
*
* Main method.
*
* #param args commandline arguments, should they be needed.
*
*/
public static void main(String[] args)
{
enum Faction
{
F1,
F2,
F3,
ALL,
;
}
enum Skill
{
SKILL_1(Faction.F1, "arbitraryArgs"),
SKILL_2(Faction.F2, "arbitraryArgs"),
SKILL_3(Faction.F3, "arbitraryArgs"),
SKILL_ALL_CAN_USE(Faction.ALL, "arbitraryArgs"),
;
private final Faction faction;
private final String arbitraryArgs;
Skill(Faction faction, String arbitraryArgs)
{
this.faction = faction;
this.arbitraryArgs = arbitraryArgs;
}
public static List<Skill> fetchAllSkillsForFaction(final Faction faction)
{
return
Arrays.stream(values())
.parallel()
.filter(each -> each.faction == faction)
.toList()
;
}
}
System.out.println(Skill.fetchAllSkillsForFaction(Faction.F1));
}
}

Related

Java class: limit instance variable to one of several possible values, depending on other instance variables

I am sorry for the vague question. I am not sure what I'm looking for here.
I have a Java class, let's call it Bar. In that class is an instance variable, let's call it foo. foo is a String.
foo cannot just have any value. There is a long list of strings, and foo must be one of them.
Then, for each of those strings in the list I would like the possibility to set some extra conditions as to whether that specific foo can belong in that specific type of Bar (depending on other instance variables in that same Bar).
What approach should I take here? Obviously, I could put the list of strings in a static class somewhere and upon calling setFoo(String s) check whether s is in that list. But that would not allow me to check for extra conditions - or I would need to put all that logic for every value of foo in the same method, which would get ugly quickly.
Is the solution to make several hundred classes for every possible value of foo and insert in each the respective (often trivial) logic to determine what types of Bar it fits? That doesn't sound right either.
What approach should I take here?
Here's a more concrete example, to make it more clear what I am looking for. Say there is a Furniture class, with a variable material, which can be lots of things, anything from mahogany to plywood. But there is another variable, upholstery, and you can make furniture containing cotton of plywood but not oak; satin furniture of oak but not walnut; other types of fabric go well with any material; et cetera.
I wouldn't suggest creating multiple classes/templates for such a big use case. This is very opinion based but I'll take a shot at answering as best as I can.
In such a case where your options can be numerous and you want to keep a maintainable code base, the best solution is to separate the values and the logic. I recommend that you store your foo values in a database. At the same time, keep your client code as clean and small as possible. So that it doesn't need to filter through the data to figure out which data is valid. You want to minimize dependency to data in your code. Think of it this way: tomorrow you might need to add a new material to your material list. Do you want to modify all your code for that? Or do you want to just add it to your database and everything magically works? Obviously the latter is a better option. Here is an example on how to design such a system. Of course, this can vary based on your use case or variables but it is a good guideline. The basic rule of thumb is: your code should have as little dependency to data as possible.
Let's say you want to create a Bar which has to have a certain foo. In this case, I would create a database for BARS which contains all the possible Bars. Example:
ID NAME FOO
1 Door 1,4,10
I will also create a database FOOS which contains the details of each foo. For example:
ID NAME PROPERTY1 PROPERTY2 ...
1 Oak Brown Soft
When you create a Bar:
Bar door = new Bar(Bar.DOOR);
in the constructor you would go to the BARS table and query the foos. Then you would query the FOOS table and load all the material and assign them to the field inside your new object.
This way whenever you create a Bar the material can be changed and loaded from DB without changing any code. You can add as many types of Bar as you can and change material properties as you goo. Your client code however doesn't change much.
You might ask why do we create a database for FOOS and refer to it's ids in the BARS table? This way, you can modify the properties of each foo as much as you want. Also you can share foos between Bars and vice versa but you only need to change the db once. cross referencing becomes a breeze. I hope this example explains the idea clearly.
You say:
Is the solution to make several hundred classes for every possible
value of foo and insert in each the respective (often trivial) logic
to determine what types of Bar it fits? That doesn't sound right
either.
Why not have separate classes for each type of Foo? Unless you need to define new types of Foo without changing the code you can model them as plain Java classes. You can go with enums as well but it does not really give you any advantage since you still need to update the enum when adding a new type of Foo.
In any case here is type safe approach that guarantees compile time checking of your rules:
public static interface Material{}
public static interface Upholstery{}
public static class Oak implements Material{}
public static class Plywood implements Material{}
public static class Cotton implements Upholstery{}
public static class Satin implements Upholstery{}
public static class Furniture<M extends Material, U extends Upholstery>{
private M matrerial = null;
private U upholstery = null;
public Furniture(M matrerial, U upholstery){
this.matrerial = matrerial;
this.upholstery = upholstery;
}
public M getMatrerial() {
return matrerial;
}
public U getUpholstery() {
return upholstery;
}
}
public static Furniture<Plywood, Cotton> cottonFurnitureWithPlywood(Plywood plywood, Cotton cotton){
return new Furniture<>(plywood, cotton);
}
public static Furniture<Oak, Satin> satinFurnitureWithOak(Oak oak, Satin satin){
return new Furniture<>(oak, satin);
}
It depends on what you really want to achieve. Creating objects and passing them around will not magically solve your domain-specific problems.
If you cannot think of any real behavior to add to your objects (except the validation), then it might make more sense to just store your data and read them into memory whenever you want. Even treat rules as data.
Here is an example:
public class Furniture {
String name;
Material material;
Upholstery upholstery;
//getters, setters, other behavior
public Furniture(String name, Material m, Upholstery u) {
//Read rule files from memory or disk and do all the checks
//Do not instantiate if validation does not pass
this.name = name;
material = m;
upholstery = u;
}
}
To specify rules, you will then create three plain text files (e.g. using csv format). File 1 will contain valid values for material, file 2 will contain valid values for upholstery, and file 3 will have a matrix format like the following:
upholstery\material plywood mahogany oak
cotton 1 0 1
satin 0 1 0
to check if a material goes with an upholstery or not, just check the corresponding row and column.
Alternatively, if you have lots of data, you can opt for a database system along with an ORM. Rule tables then can be join tables and come with extra nice features a DBMS may provide (like easy checking for duplicate values). The validation table could look something like:
MaterialID UpholsteryID Compatability_Score
plywood cotton 1
oak satin 0
The advantage of using this approach is that you quickly get a working application and you can decide what to do as you add new behavior to your application. And even if it gets way more complex in the future (new rules, new data types, etc) you can use something like the repository pattern to keep your data and business logic decoupled.
Notes about Enums:
Although the solution suggested by #Igwe Kalu solves the specific case described in the question, it is not scalable. What if you want to find what material goes with a given upholstery (the reverse case)? You will need to create another enum which does not add anything meaningful to the program, or add complex logic to your application.
This is a more detailed description of the idea I threw out there in the comment:
Keep Furniture a POJO, i.e., just hold the data, no behavior or rules implemented in it.
Implement the rules in separate classes, something along the lines of:
interface FurnitureRule {
void validate(Furniture furniture) throws FurnitureRuleException;
}
class ValidMaterialRule implements FurnitureRule {
// this you can load in whatever way suitable in your architecture -
// from enums, DB, an XML file, a JSON file, or inject via Spring, etc.
private Set<String> validMaterialNames;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
if (!validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial());
}
}
class UpholsteryRule implements FurnitureRule {
// Again however suitable to implement/config this
private Map<String, Set<String>> validMaterialsPerUpholstery;
#Overload
void validate(Furniture furniture) throws FurnitureRuleException {
Set<String> validMaterialNames = validMaterialsPerUpholstery.get(furniture.getUpholstery();
if (validMaterialNames != null && !validMaterialNames.contains(furniture.getMaterial()))
throws new FurnitureRuleException("Invalid material " + furniture.getMaterial() + " for upholstery " + furniture.getUpholstery());
}
}
// and more complex rules if you need to
Then have some service along the lines of FurnitureManager. It's the "gatekeeper" for all Furniture creation/updates:
class FurnitureManager {
// configure these via e.g. Spring.
private List<FurnitureRule> rules;
public void updateFurniture(Furniture furniture) throws FurnitureRuleException {
rules.forEach(rule -> rule.validate(furniture))
// proceed to persist `furniture` in the database or whatever else you do with a valid piece of furniture.
}
}
material should be of type Enum.
public enum Material {
MAHOGANY,
TEAK,
OAK,
...
}
Furthermore you can have a validator for Furniture that contains the logic which types of Furniture make sense, and then call that validator in every method that can change the material or upholstery variable (typically only your setters).
public class Furniture {
private Material material;
private Upholstery upholstery; //Could also be String depending on your needs of course
public void setMaterial(Material material) {
if (FurnitureValidator.isValidCombination(material, this.upholstery)) {
this.material = material;
}
}
...
private static class FurnitureValidator {
private static boolean isValidCombination(Material material, Upholstery upholstery) {
switch(material) {
case MAHOGANY: return upholstery != Upholstery.COTTON;
break;
//and so on
}
}
}
}
We often are oblivious of the power inherent in enum types. The Java™ Tutorials clearly states "you should use enum types any time you need to represent a fixed set of constants."
How do you simply make the best of enum in resolving the challenge you presented? - Here goes:
public enum Material {
MAHOGANY( "satin", "velvet" ),
PLYWOOD( "leather" ),
// possibly many other materials and their matching fabrics...
OAK( "some other fabric - 0" ),
WALNUT( "some other fabric - 0", "some other fabric - 1" );
private final String[] listOfSuitingFabrics;
Material( String... fabrics ) {
this.listOfSuitingFabrics = fabrics;
}
String[] getListOfSuitingFabrics() {
return Arrays.copyOf( listOfSuitingFabrics );
}
public String toString() {
return name().substring( 0, 1 ) + name().substring( 1 );
}
}
Let's test it:
public class TestMaterial {
for ( Material material : Material.values() ) {
System.out.println( material.toString() + " go well with " + material.getListOfSuitingFabrics() );
}
}
Probably the approach I'd use (because it involves the least amount of code and it's reasonably fast) is to "flatten" the hierarchical logic into a one-dimensional Set of allowed value combinations. Then when setting one of the fields, validate that the proposed new combination is valid. I'd probably just use a Set of concatenated Strings for simplicity. For the example you give above, something like this:
class Furniture {
private String wood;
private String upholstery;
/**
* Set of all acceptable values, with each combination as a String.
* Example value: "plywood:cotton"
*/
private static final Set<String> allowed = new HashSet<>();
/**
* Load allowed values in initializer.
*
* TODO: load allowed values from DB or config file
* instead of hard-wiring.
*/
static {
allowed.add("plywood:cotton");
...
}
public void setWood(String wood) {
if (!allowed.contains(wood + ":" + this.upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
}
public void setUpholstery(String upholstery) {
if (!allowed.contains(this.wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.upholstery = upholstery;
}
public void setMaterials(String wood, String upholstery) {
if (!allowed.contains(wood + ":" + upholstery)) {
throw new IllegalArgumentException("bad combination of materials!");
}
this.wood = wood;
this.upholstery = upholstery;
}
// getters
...
}
The disadvantage of this approach compared to other answers is that there is no compile-time type checking. For example, if you try to set the wood to plywoo instead of plywood you won’t know about your error until runtime. In practice this disadvantage is negligible since presumably the options will be chosen by a user through a UI (or through some other means), so you won’t know what they are until runtime anyway. Plus the big advantage is that the code will never have to be changed so long as you’re willing to maintain a list of allowed combinations externally. As someone with 30 years of development experience, take my word for it that this approach is far more maintainable.
With the above code, you'll need to use setMaterials before using setWood or setUpholstery, since the other field will still be null and therefore not an allowed combination. You can initialize the class's fields with default materials to avoid this if you want.

Restricting a class to a certain amount of objects in Java

I would like to ask if there is a way to restrict a certain class to make more than a certain amount of instances. And if it is possible to make the compiler ("Eclipse") underline that line, when you try to make another instance (like it is an error in the code), or something along those lines?
Just to clarify for those who would say this is a bad idea, I am making a chess game, so I need it to not be able to make more than certain amount of playing pieces.
Eclipse can't do that at compile time, because it can't tell how many times a piece of code will be executed, and thus how many instances are created.
But you can design your class to only create a given number of instances, and make it impossibale to create more. For example:
public class Limited {
public static final List<Limited> ALL_INSTANCES =
Collections.unmodifiableList(createInstances());
private int id;
private static List<Limited> createInstances() {
List<Limited> result = new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(new Limited(i));
}
}
private Limited(int id) {
this.id = id;
}
}
Since the constructor is private, the only 10 instances available are the ones in ALL_INSTANCES.
That said, that is not necessarily a good idea. Let's say you're creating a chess game. So by your logic, you shouldn't be able to create more than 2 King instances. What if your app handles 10 games at a time? Do you really want at most 2 Kings, or do you just want each ChessGame instance to have a black and a white King? Maybe all you need is something like
public class ChessGame {
private King blackKing = new King(BLACK);
private King whiteKing = new King(WHITE);
...
}
You can simply create the required number of objects - say by instantiating your class within a loop and then set a flag which is checked by the constructor so as to throw exception when an attempt is made to create one more. You can make the constructor private and have a static method to generate your instances.

Reading information to a map

I'm learning about Sets and Maps in the Introduction to Java Programming book by Daniel Liang. My professor has assigned a problem in the back of the chapter that asks me to create a program that:
Queries the user for input on name
Queries the user for gender
Using these two criteria, and this/these website(s): http://cs.armstrong.edu/liang/data/babynamesranking2001.txt
... http://cs.armstrong.edu/liang/data/babynamesranking2010.txt
I have to be able to get the ranking.
I'm supposed to get this information into an array of 10 maps.
Each map corresponds with a .txt file/year. This is where I'm having problems with. How do I do this?
The (Int) rank of the student is the value of the map, and the key is the name (String) of the baby.
The way I was thinking was to create an array of maps or maybe a list of them. So like:
List<Map<Int, String>> or <Map<Int, String>[] myArray;
Yet even after that the issue of how I get all of this information from the .txt file to my maps is a hard one for me.
This is what I've come up so far. I can't say I'm happy with it. It doesn't even work when I try to start reading information is because I haven't specified the size of my array.
public class BabyNamesAndPopularity
{
public static void main (String[] args) throws IOException
{
Map<Integer, String>[] arrayOfMaps;
String myURL = "cs.armstrong.edu/liang/data/babynamesranking2001.txt";
java.net.URL url = new java.net.URL(myURL);
Scanner urlInput = new Scanner (url.openStream());
while(urlInput.hasNext())
{
...
}
}
}
Would it be viable to make a set OF MAPS? I was kind of thinking it would be better to make a set OF maps because of the fact that sets expand as needed (according to the load factor). I just need some general guidance. Unfortunately the CS program at my university (Francis Marion University in Florence, SC) is VERY small and we don't have any tutors for this stuff.
This answer rather vague, because of broad nature of question, and it may be more suitable for
programmers SE site. Still, you may find these two points worth something.
Instead of thinking in terms of 'raw' compound collections, such as lists of maps of sets or such, try to invent set of domain types, which would reflect your problem domain, and, as the next step, implement these types using suitable Java collections or arrays.
Unit-testing and incremental refinement. Instead of immediately starting with access to remote data (via java.net.URL), start with static source of data. Idea here is to have 'reliable' and easily accessible input data hand, which would allow you to write unit tests easily and w/o access to network or even to file system, using set of domain types from 1st point, above. As you write unit tests you can invent necessary domain types/methods names in unit tests at first, then implement these types/methods, then make unit tests pass.
For example, you may start by writing following unit test (I assume you know how to organize your Java project in your IDE, so unit test(s) can be run properly):
public class SingleFileProcessingTest {
private static String[] fileRawData;
#BeforeClass
public static void fillRawData() {
fileRawData = new String[2];
// values are from my head, resembling format from links you've posted
fileRawData[0] = "Jacob\t20000\tEmily\t19999";
fileRawData[1] = "Michael\t18000\tMadison\t17000";
}
#Test
public void test() {
Rankings rankings = new Rankings();
rankings.process(fileRawData);
assertEquals("Jacob", rankings.getTop().getName());
assertEquals("Madison", rankings.getScorerOfPosition(4).getName());
assertEquals(18000, rankings.getScoreOf("Michael"));
assertEquals(4, rankings.getSize());
}
}
Of course, this won't even compile -- you need to type in code of Rankings class, code of class returned by getTop() or getScorerOfPosition(int) and so on. After you made this compile, you'll need to make test pass. But you get main idea here -- domain types and incremental refinement. And easily verifiable code w/o dependencies on file system or network. Just plain old java objects (POJOs). Code for working with external data sources can be added later on, after you get your POJOs right and make tests, which cover most parts of your use cases, pass.
UPDATE Actually, I've mixed up levels of abstraction in code above: proper Rankings class should not process raw data, this is better to be done in separate class, say, RankingsDataParser. With that, unit test, renamed to RankingsProcessingTest, will be:
public class RankingsProcessingTest {
#Test
public void test() {
Rankings rankings = new Rankings();
rankings.addScorer(new Scorer("Jacob", 20000));
rankings.addScorer(new Scorer("Emily", 19999));
rankings.addScorer(new Scorer("Michael", 18000));
rankings.addScorer(new Scorer("Madison", 17000));
assertEquals("Jacob", rankings.getTop().getName());
// assertEquals("Madison", rankings.getScorerOfPosition(4).getName());
// implementation of getScorerOfPosition(int) left as exercise :)
assertEquals(18000, rankings.getScoreOf("Michael"));
assertEquals(4, rankings.getSize());
}
}
With following initial implementation of Rankings and Scorer, this is actually compiles and passes:
class Scorer {
private final String name;
private final int rank;
Scorer(String name, int rank) {
this.name = name;
this.rank = rank;
}
public String getName() {
return name;
}
public int getRank() {
return rank;
}
}
class Rankings {
private final HashMap<String, Scorer> scorerByName = new HashMap<>();
private Scorer topScorer;
public Scorer getTop() {
return topScorer;
}
public void addScorer(Scorer scorer) {
if (scorerByName.get(scorer.getName()) != null)
throw new IllegalArgumentException("This version does not support duplicate names of scorers!");
if (topScorer == null || scorer.getRank() > topScorer.getRank()) {
topScorer = scorer;
}
scorerByName.put(scorer.getName(), scorer);
}
public int getSize() {
return scorerByName.size();
}
public int getScoreOf(String scorerName) {
return scorerByName.get(scorerName).getRank();
}
}
And unit test for parsing of raw data will start with following (how to download raw data should be responsibility of yet another class, to be developed and tested separately):
public class SingleFileProcessingTest {
private static String[] fileRawData;
#BeforeClass
public static void fillRawData() {
fileRawData = new String[2];
// values are from my head
fileRawData[0] = "Jacob\t20000\tEmily\t19999";
fileRawData[1] = "Michael\t18000\tMadison\t17000";
}
#Test
public void test() {
// uncomment, make compile, make pass
/*
RankingsDataParser parser = new RankingsDataParser();
parser.parse(fileRawData);
Rankings rankings = parser.getParsedRankings();
assertNotNull(rankings);
*/
}
}

When to use Builder implementaion from Joshua Bloch, when regular one?

I am wondering when should I use builder with static class inside and where classical one?
Implementation from Effective Java book
public class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;
public static class Builder {
//required
private final int size;
//optional
private boolean cheese = false;
private boolean pepperoni = false;
private boolean bacon = false;
public Builder(int size) {
this.size = size;
}
public Builder cheese(boolean value) {
cheese = value;
return this;
}
public Builder pepperoni(boolean value) {
pepperoni = value;
return this;
}
public Builder bacon(boolean value) {
bacon = value;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
private Pizza(Builder builder) {
size = builder.size;
cheese = builder.cheese;
pepperoni = builder.pepperoni;
bacon = builder.bacon;
}
}
Regular implementation
(just diagram)
You should consider using the Builder pattern when 1. you need to prevent the object from getting into an inconsistent state AND 2. using constructor parameters would be difficult to use/read because you have an unwieldy (large) number of properties that need to be set.
Using a normal default constructor + getters/setters could allow consumers to get into an invalid/inconsistent state. I.E. They might forget to set a very important property (like Cheese), but they aren't prevented from doing so and can create a Pizza anyway, even in a "bad" state. The burden is on the consumer to set state on the object appropriately after construction.
You can normally prevent this via Constructor parameters. E.G.
private Pizza() {}
public Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon)
{
...
}
But when you have a large number of parameters this can be difficult to read/write.
So, in summary, when you want to guarantee that your object can't be constructed in an inconsistent state AND you have too many fields to set that using constructor parameters would make it difficult to use and read, you could consider using the Builder pattern.
I'd say when you are prepared to go the extra mile to make the class constructor look natural.
Who can deny that this reads like good code?
Pizza pizza = new Pizza.Builder(10).cheese(true).peperoni(true).bacon(true).build();
I mean ... isn't that just sweet? It's even got bacon!
Second (and more common) option would be:
Pizza pizza = new Pizza(10);
pizza.setCheese(true);
pizza.setPeperoni(true);
pizza.setBacon(true);
This would be easier to work with using reflection and would therefore serialise/deserialise much more easily - but suffers from a more cumbersome and verbose construction.
Third and least flexible would be:
Pizza pizza = new Pizza(10, true, true, true);
but it is possible to provide both 2nd and 3rd mechanism together which can be a plus.
How to choose
There isn't a simple way to choose. If you want to woo your customers into buying your libraries you could offer all three methods but that would then spoil one of the major the benefits of using a Builder (which is hiding the actual constructor).
I would recommend using the 2nd method and perhaps the 3rd unless there is a good reason to take the unusual route of using a Builder.

Sorting Arraylists of custom objects without using interfaces

Sorry, this might be duplicated, I'm not sure if my previous attempt to post this went through.
Started to learn Java several weeks ago, working on one of my first assignments. :)
My question is somewhat basic, but I couldn't find its exact equivalent after looking through previously resolved topics. This isn't a real life problem, so I guess it's expected from me to tackle it in a very specific way.
So the task consisted of several steps - I had to create a superclass with a number of custom objects, add new subclasses, implement new methods to count the value of certain variables, write test classes and sort my output.
It's all been done apart from this last step. Not sure if I'm allowed to just post my problems like that on the web, but here is where I am right now:
I have something like:
public class Pants
{
public enum SizeType {SMALL, MEDIUM, LARGE, EXTRA_LARGE}
private SizeType size;
private String brand;
private String countryOfOrigin;
private String color;
private double price;
//Other variables and methods
}
public class Jeans extends Pants
{
//new variables and methods
}
public class Shorts extends Pants
{
//some more new variables and methods
}
And other similar subclasses.
import java.util.ArrayList;
public class Selection
{
public static void main(String[] args){
Jeans ex1 = new Jeans("John Lewis");
ex1.countryOfOrigin("US");
ex1.color("Navy");
ex1.setSize(Pants.SizeType.LARGE);
ex1.setprice(40);
ex1.machineWashable(true);
System.out.println(ex1);
Shorts ex2 = new Shorts("Ted Baker");
ex2.countryOfOrigin("United Kingdom");
ex2.color("White");
ex2.setSize(Pants.SizeType.MEDIUM);
ex2.setprice(30);
ex2.machineWashable(true);
System.out.println(ex2);
//..etc
ArrayList<Pants> selection = new ArrayList<Pants>();
selection.add(ex1);
selection.add(ex2);
selection.add(ex3);
selection.add(ex4);
selection.add(ex5);
System.out.println( "Size - LARGE: " );
System.out.println();
Pants.SizeType size;
size = Pants.SizeType.LARGE;
ListPants(selection,size);
I need to write a ListPants method to list objects depending on SizeType - starting with large in this case. I don't think I can implement any additional interfaces (which is what was mostly recommended in other threads).
Please see my attempt below (didn't work). Am I thinking in the right direction here, or?
public static void ListPants(ArrayList<Pants> selection, Pants.SizeType size)
{
for (Pants.SizeType sizeType : Pants.SizeType.values()) {
for (Pants pants : selection) {
if (pants.getSize().equals(sizeType)) {
System.out.println(selection.toString());
I think it's just a minor problem you're facing. You already defined the signature of the method which should print out all pants of a specific size:
ListPants(ArrayList<Pants> selection, Pants.SizeType size)
That is correct. Now, your code is looping over all pants and over all possible sizes:
public static void ListPants(ArrayList<Pants> selection, Pants.SizeType size)
{
for (Pants.SizeType sizeType : Pants.SizeType.values()) {
for (Pants pants : selection) {
if (pants.getSize().equals(sizeType)) {
System.out.println(selection.toString());
Since this looks like a homework assignment, i'll phrase my answer as a question:
Where are you using the size parameter in the method body of ListPants?
I am assuming your class cannot implement new interfaces, and not using interfaces at all.
You can use Collections.sort(List,Comparator) with a Comparator, which is built for your class.
Something like
Collections.sort(selection,new Comparator<Pants>() {
#Override
public int compare(Pants p1, Pants p2) {
//implement your compare method in here
...
}
});
If you are eager to create your own sorting algorithm, have a look of this list of sorting algorithms. Simplest to implement (though pretty slow) IMO is selection-sort

Categories