I have a class as below
public abstract class MyObjectManager {
private final Map<MyKey, MyObject> objects;
private final MySystem system;
MyObjectManager(MySystem inputSystem) {
system = inputSystem;
// also initialize the "objects" field.
}
public void write(MyKey myKey, MyObject myObject) {...}
public MyObject read(MyKey myKey) {...}
public abstract MyObject getNewestObject();
}
I need two types of ConcreteManagers which will have different map implementation, for example,
One uses new LinkedHashMap(CAPACITY_1, LOAD_FACTOR_1, true){ // override the removeEldestEntry(){// logic 1}}.
The other uses new LinkedHashMap(CAPACITY_2, LOAD_FACTOR_2, false){ // override the removeEldestEntry(){// logic 2}}.
I don't want to pass the map as a #param since the map implementation is fixed for each ConcreteManager.
Should I use a strategy pattern or factory pattern to have different map initialization?
Or should I move the objects field to each implementation classes? But their implementation of read and write methods duplicate a lot.
If I understood your question, it seems to me that you could add the map as a parameter the abstract class, then pass the concrete map instance in the children constructor. For example:
public abstract class MyObjectManager {
private final Map<MyKey, MyObject> objects;
private final MySystem system;
MyObjectManager(final Map<MyKey, MyObject> objects, MySystem inputSystem) {
this.objects = objects;
this.system = inputSystem;
}
}
public class ConcreteManager extends MyObjectManager {
public ConcreteManager(MySystem inputSystem) {
super(new LinkedHashMap(CAPACITY_1, LOAD_FACTOR_1, true), inputSystem);
}
}
This way fulfils your constraints:
The Map class is fixed to a concrete manager implementation
The objects attribute remains in the parent class
Related
I have a class that contains a collection. I want to be able to specify what implementation is used at runtime. What is the best object oriented way to accomplish this?
public class Klazz {
private class Data {
...
}
private Collection<Data> collection;
public Klazz(?) {
}
}
How can I make it so the constructor specifies what type of collection is used to implement Klazz.
In Java, List, Set and Queue Interfaces are derived from java.util.Collection Interface. Hence all the objects of any implementations of List, Set and Queue can be referenced by a Collection reference.
For a detailed explanation please refer to this very good guide at geeksforgeeks
tl;dr you can do something like:
public class Klazz {
private class Data {
...
}
private Collection<Data> collection;
public Klazz(Collection<Data> collection) {
this.collection = collection;
}
public static void main(String[] args) {
Klazz k1 = new Klazz(new ArrayList<>());
Klazz k2 = new Klazz(new HashSet<>());
}
}
The classic way to allow a Java client to choose an implementation class is to use a Factory Object supplied by the client to instantiate the object.
Something like this:
public interface CollectionFactory {
<T> Collection<T> create();
}
public class Klazz {
private Collection<Data> collection;
public Klazz(CollectionFactory <Data> factory) {
collection = factory.create();
}
}
(I haven't checked the above with the compiler, so there could be typos, etc)
Passing in a collection object is another solution. There is potentially an abstraction leakage, but the same is true with a factory object if the factory is "tricky".
It is also possible to pass a Class object and use reflection to instantiate the collection.
#Tim Biegeleisen has a point though. The Collection API does not constrain the properties of the collection a great deal, and use-cases which will work equally for collections that are lists, sets or "bags" are .... unusual.
Let's say I'm building an immutable Yahtzee scorecard class:
public final class Scorecard {
private Map<Category, Integer> scorecard = new HashMap<Category, Integer>();
public Scorecard() {
// Instantiates a new empty scorecard
}
private Scorecard(Map<Category, Integer> scorecard) {
this.scorecard = scorecard;
}
public Scorecard withScore(Category category, int[] roll) {
newScorecard = new HashMap<Category, Integer>(scorecard); // Pretend that this is a deep-copy
newScorecard.put(category, calculateScoreFromRoll(roll));
return new Scorecard(newScorecard);
}
public int getScore(Category category) {
return scorecard.get(category);
}
}
Basically I don't want to expose the internals of the class. If I didn't have a private constructor then I would need to use a public constructor with a Map argument just like the private one (and I could essentialy lose the withScore() method too) in order to allow scoring. But is this a valid way of doing factory methods?
A very common, and good pattern is to have all private constructors and public static factory methods:
public class MyClass {
private MyClass() {}
public static MyClass fromA(A foo) {
MyClass o = new MyClass();
o.field = bar; // etc
return o;
}
public static MyClass fromB(B foo) {
MyClass o = new MyClass();
o.field = bar; // etc
return o;
}
}
Note: This allows different factory methods with the same parameter types, which constructors do not allow.
Factory methods are intended to allow you to get an object without specifying the exact type.
For example, from Effective Java, 2nd edition:
The class java.util.EnumSet (Item 32), introduced in release 1.5, has no public constructors, only static factories. They return one of two implementations, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.
The existence of these two implementation classes is invisible to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved benefi- cial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet.
Using constructors instead of static methods like you suggested breaks the factory method pattern, because by using the constructor directly you are specifying an implementation.
In your case, if you want to use a factory method you would make the default constructor private so clients could not directly instantiate a ScoreCard. At this point, you're free to use whatever specific implementation of ScoreCard in the factory method. For example, if you make a second ScoreCard class that is backed with a TreeMap, you can switch which implementation of ScoreCard that the client gets just by changing the static factory.
I have a class that I wish to take two different types of inputs (via one constructor argument) and process polymorphically. Everything in the class is the same except for the handling of these two different input types, and I don't wish to duplicate all the code just due to the need for one field to have variable type. I guess my question is how to define a field to have a variable type? Or is this just bad practice? See my simple example below where I use *Map or Set* as a placeholder representing the variable type I wish to use for a field or method return type.
public abstract class ReturnTwoTypes {
public abstract Map<String,Double> *or* Set<String> runProgram();
}
private class ReturnSetType extends ReturnTwoTypes{
#Override
public Set<String> runProgram(){
return new TreeSet<String>();
}
}
private class ReturnMapType extends ReturnTwoTypes{
#Override
public Map<String, Double> runProgram(){
return new TreeMap<String, Double>();
}
}
private class UsesReturnTwoTypes{
//This class has a bunch of code I wish to reuse by not defining separate classes for both the Map and Set version of the MapOrSet field
private ReturnTwoTypes twotypes;
private Map<String,Double> *or* Set<String> mapOrSet;
public UsesReturnTwoType( ReturnTwoTypes twotypes ){
this.twotypes = twotypes;
}
public void runProgram(){
mapOrSet = twotypes.runProgram();
}
}
Make your class generic:
public abstract class ReturnTwoTypes<T> {
public abstract T runProgram();
}
private class ReturnSetType extends ReturnTwoTypes<Set<String>> {
#Override
public Set<String> runProgram(){
return new TreeSet<String>();
}
}
An attribute can not have multiple types, using a generic Object as suggested in comments and answer is really dirty. You could either use generic methods or have your method return a custom object.
You could declare them as Object and store whatever reference you want. Then you can use instanceof or getClass().getName() to check what is in there and take action appropriately.
My apologies for my limited knowledge of generics beforehand.
I have the following situation:
Class 1:
public class PostProcess implements Serializable {
public int order;
// Several other variables.
// Constructor setting vars
public PostProcess(){
}
/* Getters and setters for variables */
}
Class 2:
public class Application extends PostProcess{
public int subOrder;
// Several other variables.
// Constructor setting vars
public Application(){
}
/* Getters and setters for variables */
}
Class 3:
public class FileOperation extends PostProcess{
public int subOrder;
// Several other variables (different from class 'Application').
// Constructor setting vars
public FileOperation(){
}
/* Getters and setters for variables */
}
What I am trying to achieve in a different class is to sort a list containing a mix of 'FileOperation' and 'Application' objects defined as:
private ArrayList<? extends PostProcess> processList = new ArrayList<PostProcess>();
This sort will have to be on two fields of both of these objects, namely: 'order' and 'subOrder'. 'order' is inherited from PostProcess and 'subOrder' is defined in both the 'Application' and 'FileOperation' classes.
Throughout my journey of reading up on Generics, Comparable, Comparators and Interfaces I think I got things mixed up.
I am trying to apply a sort using:
Collections.sort(processList, new PostProcessComparator());
The PostProcessComparator is defined as:
public class PostProcessComparator implements Comparator<? extends PostProcess> {
#Override
public int compare(Object p1, Object p2) {
int mainOrderCompare = p1.getOrder().compareTo(p2.getOrder());
if (mainOrderCompare != 0) {
return mainOrderCompare;
} else {
return p1.getSubOrder().compareTo(p2.getSubOrder());
}
}
}
Questions:
I know my Comparator (and possibly more) is wrong, but I dont know where specifically. Im here to learn ;)
I noticed that defining the 'processList' List isnt the right way to do it. When I try to add an 'FileOperation' or 'Application' object to the List the compiler slaps me in the face with "no suitable method found for add(Application)" (the same for FileOperation). Did I incorrectly assume that I could use generics to declare my processList types? should be correct as both classes have PostProcess as their superclass right?
Defining the PostProcessComparator with class bounds should work in my eyes as I only want to be able to compare objects that have PostProcess as a superclass (and thus have access to the same methods).
How do I access the argumented objects in the Comparator class for p1 and p2 (because I still need to declare their types for the arguments:
#Override
public int compare(<What to put here?> p1, <And here?> p2) {
}
I really hope you guys can help!
If I was unclear in something, do let me know and ill elaborate.
Thanks!
EDIT
Thanks to NickJ and Winzu I have made the necessary changes to the comparator and the ArrayList definition.
I have moved subOrder from Application and FileOperation to the parent class (and made them protected)
Redefined parameterization of the comparator to:
public class PostProcessComparator<T extends PostProcess> implements Comparator<T>
Made use of the Integer.compare(p1.getOrder(), p2.getOrder()) for initial comparator comparisons.
Now the final challenge (compiler warnings)
Upon calling:
Collections.sort(processList, new PostProcessComparator());
I get the warning:
- [unchecked] unchecked method invocation: method sort in class Collections is applied to given types
required: List<T>,Comparator<? super T>
found: ArrayList<PostProcess>,PostProcessComparator
The parameterization is correct for this comparator in my eyes, aside from the fact that i havent checked the object types.
Where does this go wrong?
The only problem I found was that you need to parameterise PostProcessComparator like this:
public class PostProcessComparator<T extends PostProcess> implements Comparator<T> {
#Override
public int compare(T p1, T p2) {
int mainOrderCompare = p1.getOrder().compareTo(p2.getOrder());
if (mainOrderCompare != 0) {
return mainOrderCompare;
} else {
return p1.getSubOrder().compareTo(p2.getSubOrder());
}
}
}
Now the compare() method will accept the correct class (extends PostProcess) and so any public methods of PostProcess may be called from within comlpare()
Finally, your fields should not be public. I suggest making the fields protected, so subclasses may still inherit them, but encapsulation is maintained.
NickJ's answer above shows how to implement the comparator.
For what you're doing you also want to change
private ArrayList<? extends PostProcess> processList = new ArrayList<PostProcess>();.
to
ArrayList<PostProcess> processList = new ArrayList<PostProcess>();
This is why you can't add your Application et FileOperation objects into your list.
It's a bit tricky. Maybe this post can help you understand
Java using generics with lists and interfaces
You also want SubOrder in the parent class.
import java.io.Serializable;
public class PostProcess implements Serializable {
private int order;
private int subOrder;
// Several other variables.
public int getSubOrder() {
return subOrder;
}
public void setSubOrder(int subOrder) {
this.subOrder = subOrder;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
// Constructor setting vars
public PostProcess(){
}
}
Finally call the comparator like this to avoid unchcked warning:
Collections.sort(processList, new PostProcessComparator<PostProcess>());
Just change it to:
public class PostProcessComparator implements Comparator<PostProcess> {
#Override
public int compare(PostProcess p1, PostProcess p2) {
//...
}
}
That's it. You have a comparator that can compare any two instances of PostProcess (and instances of subclasses of PostProcess are instances of PostProcess).
I have an application where I need two static caches, once short-term and one long-term.
So I have an abstract class that looks something like this. My thought was that I would create two classes that inherit from this abstract class, thereby attaining my two static classes.
However, it occurs to me that I am creating 3 objects when I might be able to get by with one. But I am at a loss in how to do so. Do I want some sort of factory class?
Can someone suggest an appropriate pattern here?
public abstract class myCache {
static Map<String, Object> ObjectCache = new ConcurrentHashMap<String, Object>();
public void put(String Key, T cmsObject) {
//
}
public xxx static get(String objectKey, Class<T> type) {
//
}
}
Your design is flawed:
A cache is a cache - let the caching class take care of caching... only
Unless the number of objects is large (1000's), don't let the "number of objects created" influence your design
Only the user of your cache class needs to know or care what how the cache is being used
Thus:
public class MyClass {
private static MyCache shortTermCache = new MyCache();
private static MyCache longTermCache = new MyCache();
}
You may consider passing a time-to-live parameter into your cache class constructor to let it manage purging after a certain time.
public abstract class myCache {
static ConcurrentMap<Class<?>,Map<String, Object>> ObjectCache = new ConcurrentHashMap<Class<?>,Map<String, Object>>();
{
ObjectCache.putIfAbsent(getClass(),new ConcurrentHashMap<String,Object>());
}
public void put(String Key, Object cmsObject) {
ObjectCache.get(this.getClass()).put(key,cmsObject);
}
public Object get(String objectKey) {
return ObjectCache.get(this.getClass()).get(key);
}
}