I have a class called SalesOrder (SO), that allows users to buy several items in a single order. SO has an order number.
class SalesOrder {
public String orderNumber;
}
Each SO has many items in it, so I have created a new class OrderItem which has the item name and price.
class OrderItem {
public String name;
public double price;
}
Each SO has a order header, include user name and address. It also has a field called total price, which hold the sum of all items prices
class OrderHeader {
public String username;
public String address;
public double totalPrice;
}
After that, I added two fields to SO:
class SalesOrder {
...
public List<OrderItem> items;
public OrderHeader header;
}
Because OrderItem and OrderHeader are always used with SalesOrder and the header should return all items prices, I converted them to be be inner classes of SalesOrder.
class SalesOrder {
...
public SalesOrder() {
this.items = new ArrayList<>();
this.header = new OrderHeader();
}
public class OrderItem {
...
}
public class OrderHeader {
...
public double getTotalPrice() {
double total = 0.0;
// loop SalesOrder.items
total += items[i].price;
return total;
}
}
}
My question is whether using inner classes like this is good OOP design? If not, how should they be designed?
======= Update Some information =======
I'm very sorry that I haven't give more inforamtion.
Header and Item make they construe method private, other object can't create them without SalesOrder.
SalesOrder have a factory method
class SalesOrder {
...
public SalesOrder parseOrder(Xml xml) {
//init header and items from xml
this.header = new OrderHeader(valueFromXml, valueFromXml);
}
public class OrderHeader {
....
private OrderHeader(username, address) { ... }
}
public Class OrderItem {
...
private OrderItem(name, price) { ... }
}
}
And other object use them like this
Xml xml = orderXmlData;
SalesOrder order = SalesOrder.parseOrder(orderXmlData);
OrderItem item = order.item;
OrderHeader header = order.header;
There are a few suggestion I would have that might improve your design. Firstly, it seems unlikely to me that the totalPrice should be part of the header. It seems more likely that it is derived from the order items rather than being a component of the header. Secondly, unless you want clients of the class to create order items independent of the order then there seems no need to define them as a class. Better to convert to an interface returned from the order. Thirdly, there's no reason why Header can't be interface - this allows a client to use any class they want as a header as long as it has name and address.
So my suggestion would be something like:
class Order {
interface Item {
String getName();
double getPrice();
}
interface Header {
String getName();
Address getAddress();
}
public Order(Header header) {
...
}
public double getTotalPrice() {
return streamItems().mapToDouble(Item::getPrice).sum();
}
public void addItem(String name, double price) {
...
}
public Stream<Item> streamItems() {
...
}
}
The use of nested classes depends on the requirements. I do not see any need for the use of nested classes in your code. If it is a Java Beans class, then you should keep the classes separated so they can be reusable. Other than your last block of code with the nested classes, your design is perfect.
Related
I am setting values to an API and I need to set values for class data type variable which is an array and I need to know how to set the value?
I have tried in java, and I keep on getting compile time error
Items equipmenxxts = new Items ();
equipmenxxts.setDKU(savedRequest.DKUType());
equipmenxxts.setQuantity(savedRequest.getQuantity());
item.setEquipments(equipmenxxts);
**//error setEquipments(Items[]) in ItemOrder cannot be applied to (Items)**
api class to set values
public class ItemOrder implements java.io.Serializable {
private java.lang.String company,
private Items[] equipments; // class given below
public ItemOrder() {
}
public ItemOrder(Items[] equipments) {
this.equipments = equipments;
}
public java.lang.String getCompany() {
return company;
}
public void setCompany(java.lang.String company) {
this.company = company;
}
public Items[] getEquipments() {
return equipments;
}
public void setEquipments(Items[] equipments) {
this.equipments = equipments;
}
}
data type of this class used above
public class Items implements java.io.Serializable {
private java.lang.String DKU;
private int quantity;
public Items() {
}
public Items(String DKU, int quantity) {
this.DKU = DKU;
this.quantity = quantity;
}
}
api class to set up value
#Service("clApiService")
public class NewApiImpl implements NewApiService {
#Override
public Request completeapiNewOrderRep(ServletWebRequest webRequest) {
try {
ItemOrder item = new ItemOrder();
item.setCompany(req.getCompany());
item.setEquipments(); //error setEquipments(Items[]) in ItemOrder cannot be applied to ()**
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
I expect just to set the values of (req.setDKU and Quantity) to item.setEquipments( );
.setEquipments(Items[]) demands an array of items, but you pass only a single item.
try creating an array containing your item first:
item.setEquipments(new Items[] {equipmenxxts});
Alternatively you can create equipmentxxts as an array:
final Items[] equipmenxxts = new Items[1];
equipmenxxts[0].setDKU(savedRequest.DKUType());
equipmenxxts[0].setQuantity(savedRequest.getQuantity());
item.setEquipments(equipmenxxts);
Also, when setting a number of items this way, make sure you do not expose your class' internal state, unless you really know what you are doing—and why! You may consider a variable number of arguments for your method:
public Items[] getEquipments() {
return Arrays.copyOf(equipments, equipments.length);
}
public void setEquipments(Items... equipments) {
this.equipments = Arrays.copyOf(equipments, equipments.length);
}
Now you can either call .setEquipments(...) with an array as parameter, or with a custom number of items:
item.setEquipments(e1, e2, e3);
You may reconsider the names of your variables. I do not understand, why an ItemOrder object is called "item" - and you set "Items" objects through .setEquipments(...)
For what I studied, making a set for an array is somewhat a design error. You can, however, make a void setItems(Items i), introducing on parameters a certain index of an ItemOrder or you can make a "superset", which is not a real set:
public void superSet(ItemOrder io){
this.equipments=io.setEquipments(Items[] i);
}
Suppose I have a simple Java Enum:
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
}
I would now like to introduce a "category" to this enum, for example to make the distinction between liquid items and solid items. I found two ways of doing this within the enum class, see below. However, both suffer from the same anti-pattern: if the amount of categories or amount of items ever increases/decreases (imagine 100 item types with 10 categories!), I've got a lot of updating to do. What patterns can I use to design this enum as cleanly and re-usable as possible?
First approach: Add additional properties to the enum
public Enum itemType
{
FRUITS("fru",false),
VEGETABLES("veg",false),
LIQUOURS("liq",true),
SODAS("sod",true);
private String dbCode;
private boolean liquid;
public ItemType(String dbCode, boolean liquid){
this.dbCode = dbCode;
this.liquid = liquid;
}
public String getDbCode(){
return this.dbCode;
}
public boolean isLiquid(){
return this.liquid;
}
}
Second approach: Use static methods to ask about subcategories
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
public static boolean isLiquid(ItemType type){
switch(t){
case SODA:
case LIQOURS: return true;
default: return false;
}
}
How about using an EnumSet for that?
public enum ItemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
public static final EnumSet<ItemType> LIQUIDS = EnumSet.of(LIQUOURS, SODAS);
// ...
}
Then you can use ItemType.LIQUIDS.contains(someItemType) to check if someItemType is a "liquid".
I would do something like:
enum Category {
LIQUID, SOLID;
}
enum ItemType {
FRUITS("fru", SOLID),
VEGETABLES("veg", SOLID),
LIQUOURS("liq", LIQUID),
SODAS("sod", LIQUID);
private String dbCode;
private Category category;
public ItemType(String dbCode, Category category){
this.dbCode = dbCode;
this.category = category;
}
/* getters / setters */
}
That would allow, for example, that you can add new products and categories (e.g. BUTANE("but", GAS)) without having to modify the existing code (as would happen in Approach 2).
On the other hand, if the number of categories and items is long and changing, I would consider to use a SQL database.
Since you are modeling something that has no logic that can be encoded in an algorithmic way (i.e. there's no algorithm that would figure out that "sod" is liquid and "veg" is not) there is no way around enumerating all related pairs of (item, category) in one way or the other.
There are three approaches to implementing it:
Enumerate categories on item's side - this is what your code does in both cases, or
Enumerate items on category's side - this would build an enum of categories, and attach a full list of items to each of them, or
Enumerate item+category pairs independently - this approach may be useful when storing item/category mapping in the database or in a configuration file.
I would recommend taking the third approach as it is the most "symmetric" one. Make a table for categories with category codes, and add a "cross-table" (or a cross-file) that has all pairs of categories and their corresponding items. Read the cross table/file at startup, and set up the dependencies on both sides.
public Enum ItemType {
FRUITS("fru")
, VEGETABLES("veg")
, LIQUOURS("liq")
, SODAS("sod");
public void addCategory(ItemCategory category) ...;
public EnumSet<ItemCategory> getItemCategories() ...;
}
public Enum ItemCategory {
LIQUIDS("liq")
, SNACKS("snk")
, FAST("fst");
public void addItem(ItemType type) ...;
public EnumSet<ItemType> getItemTypes() ...;
}
Cross-file or cross-table may look like this:
liq liq
sod liq
fru snk
fru fst
sod fst
You process it by enumerating pairs, and calling addCategory on the pair's item side, and calling addItem on the pair's category side.
These were three excellent answers, but I think I can combine all three in one nice package:
public enum ItemType {
FRUITS("fru",PERISHABLE),
VEGETABLES("veg",PERISHABLE),
LIQUOURS("liq",LIQUIDS),
SODAS("sod",LIQUIDS),
FRESH_SQUEEZED_ORANGE_JUICE("orgj",LIQUIDS,PERISHABLE);
private final String dbCode;
private final EnumSet<ItemCategory> categories;
private static final Map<ItemCategory,Set<ItemType>> INDEX_BY_CATEGORY = new EnumMap<>(ItemCategory.class);
ItemType(String dbcode,ItemCategory... categories) {
this.dbCode = dbcode;
this.categories = EnumSet.copyOf(Arrays.asList(categories));
//for (ItemCategory c:categories) {
// // Illegal Reference to Static Field!
// INDEX_BY_CATEGORY.put(c, this);
//}
}
static {
for (ItemCategory c:ItemCategory.values()) {
INDEX_BY_CATEGORY.put(c, EnumSet.noneOf(ItemType.class));
}
for (ItemType t:values()) {
for (ItemCategory c:t.categories) {
INDEX_BY_CATEGORY.get(c).add(t);
}
}
}
public boolean is(ItemCategory c) {
return INDEX_BY_CATEGORY.get(c).contains(this);
}
public Set<ItemType> getAll(ItemCategory c) {
return EnumSet.copyOf(INDEX_BY_CATEGORY.get(c));
}
public String getDbCode() {
return dbCode;
}
}
Now,
we can easily ask about additional subcategories without writing the code for it: boolean isVegetableLiquid = VEGETABLES.is(LIQUIDS);
we can easily assign not only one, but multiple categories to an item as you can see for FRESH_SQUEEZED_ORANGE_JUICE.
we are using EnumSet and EnumMap for performance, including their methods like contains.
we absolutely are minimizing the amount of code required to add an additional item. This could be further minimized by setting this up by database or configuration. However, in that case we would have to avoid the use of Enum as well.
I am making an inventory system.
I want to ensure that objects I am creating (Ingredients) all have unique names. In other words, I want to make sure that there are never two Ingredients that have the same name in the whole program. Currently I have the following class:
package ingredient;
import java.util.HashSet;
public class Ingredient {
private final String name;
private final double price;
private static HashSet<String> names = new HashSet<String> ();
private Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public static Ingredient createIngredient(String ingr_name, double ingr_price) {
if (names.contains(ingr_name)) {
return null;
} else {
names.add(ingr_name);
return new Ingredient(ingr_name, ingr_price);
}
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Then, when I go to actually make new ingredients, I make statements such as :
Ingredient egg = Ingredient.createIngredient("egg", 1);
Is this okay design? I suppose I am concerned because returning "NULL" might not be the best practice here.
I cant comment, but whatever...
I would go about this by storing all of the Ingredients in a different class, then you wouldn't need all this static nonsense. In the class where you actually create a new Ingredient (Ingredient egg = Ingredient.createIngredient("egg", 1);) you could maybe create an ArrayList of ingredients like so:
ArrayList<Ingredient> ingredients = new ArrayList<>();
Then when you make a new Ingredient you would just have to make sure you add it to the ArrayListand when you do so, check that none of the ingredients are already there, maybe something like this:
createIngredient("egg", 1);
or
Ingredient egg = createIngredient("egg", 1);
...
private Ingredient createIngredient(String ingr_name, double ingr_price){
for(Ingredient i : ingredients){
if(i.getName().equals(ingr_name)){
return null;
}
}
Ingredient newing = new Ingredient(ingr_name, ingr_price);
ingredients.add(newing);
return newing;
}
Then the Ingredient class could be cut down to something like this:
package ingredient;
public class Ingredient {
private final String name;
private final double price;
public Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
And then you could access each individual Ingredientwith a method to run through the ArrayList and find the Ingredient with the name your looking for:
public Ingredient findIngredient(String name){
for(Ingredient i : ingredients){
if(i.getName().equals(name)){
return i;
}
}
return null;
}
I would recommend either
A) returning the already created ingredient
Or if that would confuse the caller,
B) throwing an exception
This can be a simple IllegalArgumentsException, or depending on your needs, a custom exception class.
I am attempting to create an inventory tracking system. I have a class (in Java) called "InventoryItem" with the properties of name and quantity.
This works fine for simple objects, but what if my inventory item contains other inventory items, for example, a server with RAM?
Should I be creating my own datatype, or is there a better way to do this (linked listed maybe)? should my class extend whatever that datatype is or should I not bother creating my own class?
My class so far:
public class InventoryItem {
private String name;
private int quantity;
private InventoryItem childInventoryItem;
// CONSTRUCTORS
public InventoryItem() {
}
public InventoryItem(int quantity, String name) {
this.quantity = quantity;
this.name = name;
}
//GETTERS
public String getName() {
return name;
}
public int getQuantity() {
return quantity;
}
//SETTERS
public void setName(String name) {
this.name = name;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
A tree is usually what is involved in any parent-child relationship. If you aren't doing anything complicated, you can simply maintain an internal list that is basically List<InventoryItem> which contains any child items.
So all you would add to your class is something like this:
public class InventoryItem {
...
private List<InventoryItem> composingItems = new ArrayList<>(); //if still using Java 6 this must be new ArrayList<InventoryItem>();
...
public void addComposingItem(InventoryItem composingItem) {
composingItems.add(composingItems);
}
public List<InventoryItem> getComposingItems() {
//Enforce immutability so no one can mess with the collection. However
//this doesn't guarantee immutability for the objects inside the list;
//you will have to enforce that yourself.
return Collections.umodifiableList(composingItems);
}
}
There are many ways you can do this. I think the easiest way would be to create an array list.
ArrayList<InventoryItem> childInventory = new ArrayList<InventoryItem>();
Then create a setter that adds inventory items to this array
public void addChildItem(InventoryItem child)
childInventory.add(child);
This way you would have a list of all of the child items within the item. You could also make a method to return a list of all of the child items in either an array or an ArrayList.
public ArrayList<InventoryItem> getChildItems()
return childInventory;
How do i set the sort order to DESC as default? This sort doesn't really do anything but make postedAt the only sortable item:
<div id="crudListTable">
#{crud.table fields:['title', 'postedAt'], sort:['postedAt']}
#{crud.custom 'postedAt'}
${object.postedAt.format("dd-MM-yyyy")}
#{/crud.custom}
#{/crud.table}
</div>
You can do this on the Entity if you want:
#Entity
public class Course {
...
#ManyToMany
#OrderBy("lastname ASC")
public List<Student> students;
...
}
Could you try to set the order attribute to DESC?
#{crud.table fields:['title', 'postedAt'], sort:['postedAt'], order: 'DESC'}
...
#{/crud.table}
Here is my hack:
First thing you have to do is to implement Comparable on the class that you want to sort, and further create a compareTo method.
Next, enter the crud module in your project. In app/views/tags.crud you will find a file called relationField.html, which handles the fields for adding relations. This file is divided into two parts, one for creating select-boxes with multiple=true, and one for creating dropdown select boxes. If you want both of these sorted you will have to edit in both these cases.
Substitute %{ _field.choices.each() { } with %{ _field.choices.sort().each() { }% (basically adding groovy syntax for sorting a collection), and the input fields will be sorted.
Full example of Java-classes:
Referencing class:
#Entity
public class Book extends Model {
#Required
public String title;
#Required
#ManyToMany(cascade=CascadeType.PERSIST)
public List<Author> authors;
#Required
#ManyToOne
public Publisher publisher;
//omitted
}
Referenced class:
public class Author extends Model implements Comparable {
#Required
public String firstName;
#Required
public String lastName;
public int compareTo(final Author otherAuthor) {
if (this.lastName.equals(otherAuthor.lastName)) {
if (this.firstName.equals(otherAuthor.firstName)) {
return 0;
} else {
return this.firstName.compareTo(otherAuthor.firstName);
}
} else {
return this.lastName.compareTo(otherAuthor.lastName);
}
}
//omitted
}
This structure compared with the hack on relationField.html will make the possibilities in the select appear sorted.