I have a program that recruits witness accounts of a crime scene from various subjects.
Every kind of subject has its own Report class. For example:
public class EyeWitnessReport extends AbstractReport {
private String details;
private String relation;
private QuestioningResults questioningResults;
private Integer stressLevel;
private EnumSet<CrimeFacts> crimeFacts;
private Religion religion; //to see if swearing on the Bible is an issue
}
public class ExpertWitnessReport extends AbstractReport {
private Expertise fieldOfExpertise;
private boolean relatedToAccused;
private List<Conflict> conflicts;
}
The main function of my app takes all the reports and merges them together into one CrimeScene class that contains all the details of the crime scene (by priorities, etc)
public CrimeScene mergeReports(List<AbstractReport> sources){
//takes all the reports in sources and returns one CrimeScene with the most relevant fields
}
My question is, the CrimeScene class is basically made up of data from the reports, after certain logic. It has 70 possible fields (the fields of every type of report) although on average only around 10-20 are filled.
Plus it has 2-3 fields of it's own (creationTime, reportingOfficer, etc...)
How should I go about implementing it?
Is it a better implementation to make CrimeScene a regular class (70 hardcoded fields, of which no more than 20 will be filled on average), or a dynamic class, using DynaBean, Map<String, Object> or something of the sort?
It is better to use class with properties or maybe some composite class which would allow you to limit number of properties in a single class due to composition.
With this approach you (and any other developer) will know exactly which properties and of which types does object have.
If you go with map then you will lose type safety because you will have to use Map<String, Object> to accommodate values of properties of different types. Also with map approach the only way to know which properties exist (which keys are added to the map at runtime) is to debug your code.
Related
I learn how to handle properties, on a test project, that are written in a yaml file.
I know that it is very similar to handling JSON files.
I learned that I have to write a class/objects composition model that will mirror the yaml configuration.
I did it, and I have to say that everything works pretty well but suddenly I've met a difficulty.
I mean, what if I will add a new property to yaml, that will not have a mirror object in a composition model?
I want to write a code that will read the property just after I will add it to yaml, and I have no idea how to do that.
Does anybody have some suggestions?
If you're looking for named properties, you're out of luck. You need them in your class. However, you can use some more generic collections instead, and fetch the properties by name, assuming they're something simple like key/value.
For example, if you have YAML like this:
size: 16
crust: "wheat"
toppings:
"cheese": "normal"
"pepperoni": "normal"
"onion": "normal"
"mushroom": "extra"
You could have a fixed set of toppings like this:
public class Pizza
{
private int size;
private String crust;
private Toppings toppings;
}
public class Toppings
{
private String cheese;
private String pepperoni;
private String onion;
private String mushroom;
}
But in reality, we all know everyone wants something different on their pizza. If you were to only have the same pizza for everyone, you'd go out of business. So, you need something a little more flexible for toppings. Sometimes they'll be there, sometimes they won't be. So, you have to use a more flexible data structure:
public class Pizza
{
private int size;
private String crust;
private Map<String, String> toppings;
}
Most serializers (e.g. Jackson) know what to do with collections like Map or List. They'll allow you to add whatever property you want, so long as it fits the form/type of the collection.
If I need to capture the name (first, middle and last) of a Person, I thought of the following possibilities:
Use a Map<String, String> name;
Use a separate Name class.
If I use a Map, I can store the names like this:
name.put("first", "xyz")
name.put("middle", "abc")
name.put("last", "nhf");
Then in the Person class:
class Person
{
Map<String, String> name;
//below write setter and getter for it.
}
If I use a Name class, I can store like this:
class Name
{
String first;
String middle;
String last;
//Below write setters and getters for them.
}
Then in the Person class:
class Person
{
Name name;
//below write setter and getter for it.
}
I wanted to know which one is a better way of doing and why. Also if there is any other better way of doing this.
Maps are great for flexibility. I.e., if you don't know exactly what keys you're going to have. E.g., if one person will have a Christian name, another will have a nickname and a third will have a paternal and maternal surnames, a Map may make sense. In your example, everybody has a first, middle and last name (although some of them may possibly be null?), so a map just adds redundant complication (as as noted here on the thread, memory consumption). Frankly, unless you have some special use for the entire Name as an object (e.g., using it as a key in a map), I'd just place three string members directly in the Person class.
This is entirely up to you -- there is no "best" answer here. It depends on how you are using the class.
Generally you do not want to hide things in a map like that. It makes more work to ensure things are correct. What if you accidentally add an incorrect key to the map? Your data structures could get out of whack easily with that method.
Personally, I would most likely just have the 3 values on the Person class directly and not bother with a Name class or a Map.
Once again, I am using words like "most likely" and "generally" because I do not know how the class is being used.
Creating a map is quite memory consuming, compared to creating a name class.
It's better to create the name class, (for one thing, less lines of code :P), mainly because it's less prone to be changed mid-run, unlike the map.
The entries in a map might be accidentally changed during runtime, which could cause problems. If the Name class doesn't have setters, then this can't happen.
I want to create a dynamic sql java application. Normaly i create a java pojo with hard coded columns. For Example:
public class DbEntry{
private int id;
private String name;
public setter and getter
}
Now, the problem is, that the user can change the Database columns as he need. For example, he can add new columns if he need and so on. But if he change the columns the hard coded pojo cant representate the whole db entry. I have read over dynamic byte code creation, but i dont really want to use this, if there is an other/better solution.
Consider this class:
public class DbEntry{
List<Integer> integerList;
List<String> strList;
public Integer getInt(int index){
return integerList.get(index);
}
public String getStr(int index){
return strList.get(index);
}
//todo: add some constructors/factory methods
}
For fixed columns, you can write some global constants like staic int I_ID=0 and static int I_NAME=0. So you can get the id and name of an DbEntry by calling dbEntry.getInt(I_ID) and dbEntry.getStr(I_NAME)
For changeable columns you can use a List<String>, add new column names to the list and then you can call dbEntry.getStr(collst.indexOf("name"))
Or you can write a class using strings as keys, so you can call dbEntry.getStr("name"), e.g.:
public class DbEntry{
Map<String,Integer> integerMap;
Map<String,String> strMap;
public Integer getInt(String key){
return integerMap.get(key);
}
public String getStr(String key){
return strMap.get(key);
}
//todo: add some constructors/factory methods
}
This class looks more straightforward but it wastes some memory. Because every dbEntry in the same table has the same set of column names. A single list is enough for storing the column names of a table. HashMap uses more memory than ArrayList. Despite this disadvantage, what data structures to use still depends on your requirements.
Or you may want to make it an interface with getInt, getStr, getDate, getBlob, so you can have the flexibility by implementing the interface using different data structures.
I have seen this done, and it is a lot of work. What you end up doing is having a dynamic model, typically modelling classes and attributes. You expose the Classes and Attributes (and their definition) to a sysadmin role.
The rest of the application sends and retrieves instance data using this dynamic model. As a start, you won't have static Java classes representing them. In your above example, the DbEntry doesn't exist. You'll end up with a generic Model Object that allows you to return DbEntry objects in a common model. Something like
class DynamicObject {
ClassDefinition getClass(); // a ClassDefinition that contains details about DbEntry
Collection<AttributeDetails> getAttributes();
AttributeValue getValue(AttributeDetails details);
void setValue(AttributeDetails details, AttributeValue value);
}
This above is all bespoke code written/defined by you. I am unaware of any third party framework that provides this to you. That said, I haven't looked very hard.
The bottom line is, for what you want to do, the Classes and Attributes end up being modelled by the application and the rest of the application works off that model. Only by doing that, will you prevent the need for making static Java changes when the model changes.
It is not trivial, and carries with it a fair amount of maintenance. I have seen this done, and over time it did become a fairly arduous task to maintain.
I am working on a project to help get me back into Java coding and it is a text based game (I know, not much, but I have to start somewhere).
Anyway, I have come across a problem. I need to be able to put names of parts (it is a hardware tycoon game) and prices along with them into an array. For example, a desktop computer has parts such as a CPU, and the game would list your CPU choices. I need a way of storing this data, and it's pretty complicated to me because I need to not only store all of the names of CPUs for the player's benefit, but also store the prices alongside the names. On top of that, I have multiple product types such as desktops, laptops, and consoles, which each pretty much have different part names and prices.
I thought of a 3 dimensional array to store the product types such as desktop (in columns), the part names (in rows), and the prices (behind the rows, if that makes sense in a 3 dimensional way. But I do not know how to initialize such an array and how to set the values on initialization.
Also, I thought of creating classes for each product type and putting arrays in each class to define parts and prices (2d arrays), but it is still complex and I would like to know how to sort this data and potentially make a system where certain parts are unlocked as game time progresses. Thank you in advance.
I might take you in a different direction. Why don't you create classes and objects? Then create instances of those objects? Java is an object oriented language. Creating objects would allow you to hold all of the values you need. Quick example,
Public abstract class CPU {
// Declare fields
private float price;
private String name;
// Declare constructors
}
Public class intelCPU extends CPU {
// GetPrice
public int getPrice() {
return price;
}
// Set Name
public void setName(n) {
name = n;
}
}
I agree with #Regis that you should be using a custom object to describe your products, and then storing them in a some kind of data structure.
However, you can solve the problem you described by declaring a multi-dimensional array of the type "Object," which will allow you to put basically anything into it, even primitives (autoboxing and autounboxing was added in Java 5 and will seamlessly translate between primitive types like int and the Java wrapper class for that type, like java.lang.Integer).
Of course such an array would be very weakly typed and there would be nothing stopping you from adding doubles to the product name column, or vice versa. You'd also have to do a lot of casting, which is a code smell.
How about using a Map instead. A Map let's you store information in Key/Value pairs. You can make a Map that has a key type of String (that represents the product type) and a value type of Map (that represents individual products) that has a String key (product name) and a value that stores the price (perhaps a double) or the info (as a String or something). That way you can do something like outerMap.get("laptops").get("laptop1") and that would return the information of laptop1. The first get, gets the Map that contains all of the laptop products (or whatever product type ypu would want). The second returns the information for the specific product in that category.
You can implement this like this.
Map<String, Map<String, String>> productMap = new HashMap<>();
Using this you would place a product type like this:
productMap.put("laptops", new HashMap<String,String>());
And then add a product like this:
productMap.get("laptops").put("laptop1","This is information about laptop 1");
PS: In case you aren't aware, the reason I used = new HashMap instead of = new Map is because Map is an interface not a class. A HashMap is a class that implements the Map interface.
More than multidimensional arrays, you could tag this with Object Oriented Programming (OOP). From your description it looks like you would need a class hierarchy, something like:
abstract class Product
class Desktop extends Product
class Laptop extends Product
class Console extends Product
Put all common fields/methods that can be used by Desktop, Laptop, Console etc into your Product class.
//Just an example
abstract class Product {
String name;
ArrayList<Component> components;
public String getName(){
return name;
}
}
Since each product has several components the product needs to have a list of components as shown above.
abstract class Component{
Double price;
public Double getPrice(){
return price;
}
}
Now you can have components like CPU, Power supply etc, they have some common behavior and fields like price that is put into Component class. Any specialized behavior / fields for each component can be put into the corresponding class like clock frequency shown below:
class CPU extends Component {
Double clockFreq;
}
So if your list of parts is 3 items long it could be written to a text file like so:
name,type,price
intelCPU6600,CPU,200
amdCPU7789,CPU,150
PS1Power,PSU,120
newPart,unknown,500
This list could be 100's of items without any problem. Read it into your program using Scanner & for each line do something like:
String line = scanner.nextLine();
String[] fields = line.split(",");
if("CPU".equals(fields[1]){
CPU cpu = new CPU(fields[0],fields[1],fields[2]);
//Product is an object of the class Product that you should have created earlier
product.components.add(cpu);
} else if("PSU".equals(fields[1]) {
PSU psu = new PSU(fields[0],fields[1],fields[2]);
product.components.add(psu);
} //..so on
if there is a generic product that you don't have a class for that's where the abstract class can be used:
if("unknown".equals(fields[1]){
Component comp = new Component(fields[0],fields[1],fields[2]);
product.components.add(comp);
}
Let's say I have a class Item. Items have object attributes and collection of other objects attributes:
public class Item
{
//Object attributes
String name;
int id;
Color color;
//Collection of object attributes
List<Parts> parts;
Map<int,Owner> ownersById;
}
I have a fairly simple web application that allows crud operations on these items. This is split up into separate operations:
a page where you can update the simple object attributes (name, id...).
a page where you can edit the collection of parts.
a page where you can edit the map of owners.
Because the server load was getting too high, I implemented a cache in the application which holds the "most recently used item objects" with their simple attributes and their collection attributes.
Whenever an edit is made to the name of an item, I want to do the following do things:
Persist the change to the item's name. This is done by converting the item object to xml (without any collection attributes) and calling a web service named "updateItemData".
Update the current user's cache by updating the relevant item's nme inside the cache. This way the cache stays relevant without having to load the item again after persisting it.
To do this I created the following method:
public void updateItem(Item itemWithoutCollectionData)
{
WebServiceInvoker.updateItemService(itemWithoutCollectionData)
Item cachedItemWithCollectionData = cache.getItemById(itemWithoutCollectionData.getId());
cachedItemWithCollectionData.setName(itemWithoutCollectionData.getName());
cachedItemWithCollectionData.setColor(itemWithoutCollectionData.getColor());
}
This method is very annoying because I have to copy the attributes one by one, because I cannot know beforehand which ones the user just updated. Bugs arised because the objects changed in one place but not in this piece of code. I can also not just do the following: cachedItem = itemWithoutCollectionData; because this would make me lose the collection information which is not present in the itemWithoutCollectionData variable.
Is there way to either:
Perhaps by reflection, to iterate over all the non-collection attributes in a class and thus write the code in a way that it does not matter if future fields are added or removed in the Item class
Find a way so that, if my Item class gains a new attribute, a warning is shown in the class that deals with the caching to signal "hey, you need to update me too!")?
an alternative which might seem a bit overkill: wrap all the non-collection attributes in a class, for example ItemSimpleData and use that object instead of separate attributes. However, this doesn't work well with inheritance. How would you implement this method in the following structure?
classes:
public class BaseItem
{
String name;
int id;
}
public class ColoredItem
{
Color color;
}
There many things that can be done to enhance what you currently have but I am going to point out just two things that may help you with your problem.
Firstly, I am assuming that public void updateItem is a simplified version from your production code. So; make sure this method is thread safe, since it is a common source or problems when it comes to caching.
Secondly, you mentioned that
Perhaps by reflection, to iterate over all the non-collection
attributes in a class and thus write the code in a way that it does
not matter if future fields are added or removed in the Item class.
If I understand the problem correctly; then, you can easily achieve this using BeanUtils.copyProperties() here is an example:
http://www.mkyong.com/java/how-to-use-reflection-to-copy-properties-from-pojo-to-other-java-beans/
I hope it helps.
Cheers,