I have controller method that get data from request and based on subject variable from request decide to call a function. (for project need I cannot use seperate controller method for each subject variable)
For now I used switch but I think it breaks Open Closed Principle (because every time new type of subject added I have to add new case to switch) and not good design, How can I refactor this code?
Subject subject = ... //(type of enum)
JSONObject data = request.getData("data");
switch(subject) {
case SEND_VERIFY:
send_foo1(data.getString("foo1_1"), data.getString("foo1_2"));
break;
case do_foo2:
foo2(data.getInt("foo2_b"), data.getInt("foo2_cc"));
break;
case do_foo3:
do_foo3_for(data.getString("foo3"));
break;
// some more cases
}
While I am not sure about which OO principle this snippet violates, there is indeed a more roust way to achieve the logic: tie the processing for each enum value to the enum class.
You will need to generalize the processing into an interface:
public interface SubjectProcessor
{
void process(JSONObject data);
}
and create concrete implementations for each enum value:
public class SendVerifySubjectProcessor implements SubjectProcessor
{
#Override
public void process(JSONObject data) {
String foo1 = data.getString("foo1_1");
String foo2 = data.getString("foo1_2");
...
}
}
once you have that class hierarchy tree, you can associate each enum value to a concrete processor
public enum Subject
{
SEND_VERIFY(new SendVerifySubjectProcessor()),
do_foo2(new Foo2SubjectProcessor()),
...
private SubjectProcessor processor
Subject(SubjectProcessor processor) {
this.processor = processor;
}
public void process(JSONObject data) {
this.processor.process(data);
}
}
This eliminates the need for the switch statement in the controller:
Subject subject = ... //(type of enum)
JSONObject data = request.getData("data");
subject.process(data);
EDIT:
Following the good comment, You can utilize the java.util.function.Consumer functional interface instead of the custom SubjectProcessor one. You can decide whether to write concrete classes or use the lambda expr construct.
public class SendVerifySubjectProcessor implements Consumer<JSONObject>
{
#Override
public void accept(JSONObject data) {
String foo1 = data.getString("foo1_1");
String foo2 = data.getString("foo1_2");
...
}
}
OR
public enum Subject
{
SEND_VERIFY(data -> {
String foo1 = data.getString("foo1_1");
String foo2 = data.getString("foo1_2");
...
}),
...
private Consumer<Subject> processor
Subject(Consumer<Subject> processor) {
this.processor = processor;
}
public void process(JSONObject data) {
this.processor.accept(data);
}
}
// SubjectsMapping.java
Map<Subject, Consumer<JSONObject>> tasks = new HashMap<>();
tasks.put(SEND_VERIFY,
data -> send_foo1(data.getString("foo1_1"), data.getString("foo1_2")));
tasks.put(do_foo2,
data -> foo2(data.getInt("foo2_b"), data.getInt("foo2_cc")));
tasks.put(do_foo3, data -> do_foo3_for(data.getString("foo3")));
// In your controller class where currently `switch` code written
if (tasks.containsKey(subject)) {
tasks.get(subject).accept(data);
} else {
throw new IllegalArgumentException("No suitable task");
}
You can maintain Map<Subject, Consumer<JSONObject>> tasks configuration in separate class rather than mixing with if (tasks.containsKey(subject)) code. When you need another feature you can configure one entry in this map.
Answers of others seems to be great, as an addition I would suggest using EnumMap for storing enums as keys as it might be more efficient than the standard Map. I think it's also worth mentioning that the Strategy Pattern is used here to achieve calling specific actions for each key from Map without the need of building long switch statements.
Related
I want to use Stream API to keep track of a variable while changing it with functions.
My code:
public String encoder(String texteClair) {
for (Crypteur crypteur : algo) {
texteClair = crypteur.encoder(texteClair);
}
return texteClair;
}
I have a list of classes that have methods and I want to put a variable inside all of them, like done in the code above.
It works perfectly, but I was wondering how it could be done with streams?
Could we use reduce()?
Use an AtomicReference, which is effectively final, but its wrapped value may change:
public String encoder(String texteClair) {
AtomicReference<String> ref = new AtomicReference<>(texteClair);
algo.stream().forEach(c -> ref.updateAndGet(c::encoder)); // credit Ole V.V
return ref.get();
}
Could we use reduce()?
I guess we could. But keep in mind that it's not the best case to use streams.
Because you've mentioned "classes" in plural, I assume that Crypteur is either an abstract class or an interface. As a general rule you should favor interfaces over abstract classes, so I'll assume the that Crypteur is an interface (if it's not, that's not a big issue) and it has at least one implementation similar to this :
public interface Encoder {
String encoder(String str);
}
public class Crypteur implements Encoder {
private UnaryOperator<String> operator;
public Crypteur(UnaryOperator<String> operator) {
this.operator = operator;
}
#Override
public String encoder(String str) {
return operator.apply(str);
}
}
Then you can utilize your encoders with stream like this:
public static void main(String[] args) {
List<Crypteur> algo =
List.of(new Crypteur(str -> str.replaceAll("\\p{Punct}|\\p{Space}", "")),
new Crypteur(str -> str.toUpperCase(Locale.ROOT)),
new Crypteur(str -> str.replace('A', 'W')));
String result = encode(algo, "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system");
System.out.println(result);
}
public static String encode(Collection<Crypteur> algo, String str) {
return algo.stream()
.reduce(str,
(String result, Crypteur encoder) -> encoder.encoder(result),
(result1, result2) -> { throw new UnsupportedOperationException(); });
}
Note that combiner, which is used in parallel to combine partial results, deliberately throws an exception to indicate that this task ins't parallelizable. All transformations must be applied sequentially, we can't, for instance, apply some encoders on the given string and then apply the rest of them separately on the given string and merge the two results - it's not possible.
Output
EVERYPIECEOFKNOWLEDGEMUSTHWVEWSINGLEUNWMBIGUOUSWUTHORITWTIVEREPRESENTWTIONWITHINWSYSTEM
Is there anyway to avoid these if conditions? because there may be different type of objects coming in.
if ("OpenOrder".equals(order.getClass().getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
}
if ("ExecutionOrder".equals(order.getClass().getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
You can use a Router pattern to do this. Simple add the computations in a Map like this:
Map<String, Function> router = new HashMap<>();
router.put("OpenOrder", (value) -> OpenOrderBuilder.createOFSMessage((OpenOrder) value));
router.put("ExecutionOrder", (value) -> ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order));
And you can route the order using the String key. Here is a "OpenOrder" example:
String result = (String) router.get("OpenOrder").apply(order);
There are many ways to do it. Which one to choose, depends on your needs and in this case in particular on how many different types of objects you will have.
I suggest looking at concepts like interfaces and inheritance and on specific design patterns.
One approach I tend to like, although still not perfect, works as follows:
interface Order {
}
interface OrderBuilder<T> {
T forType();
Object createOFSMessage(Order order);
}
class OpenOrderBuilder<OpenOrder> implements OrderBuilder {
#Override
OpenOrder forType() {
return OpenOrder.class;
}
...
}
class ExecutionOrderBuilder<ExecutionOrder> implements OrderBuilder {
#Override
ExecutionOrder forType() {
return ExecutionOrder.class;
}
...
}
class MyProcessor {
Map<Class, OrderBuilder> obs;
public void initialize() {
List<OrderBuilder> builders = new ArrayList<>();
builders.add(new OpenOrderBuilder());
builders.add(new ExecutionOrderBuilder());
obs = new HashMap<Class, OrderBuilder>();
for(OrderBuilder b : builders) {
obs.put(b.forType(), b);
}
}
public Object createOFSMessage(Order order) {
return obs.get(order.getClass()).createOFSMessage(order);
}
}
In the above example, adding a new implementation would just consist of adding an entry to the builders collection. While in the example above it's done manually, normally this is done through Dependency Injection and frameworks like spring (in which case, the initialize method may turn into a constructor with builders as an #Autowired argument).
There are of course other ways, some more simple some more complicated. The best way really depends on what you have to do and one key rule: the less code you have the better.
First one should not forget the switch-on-string:
switch (order.getClass().getSimpleName()) {
case "OpenOrder":
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
case "ExecutionOrder":
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
The code however shows inheritance being used in combination with static child class factories. Evidently a createOFSMessage is not desired in the Order base class.
Then use a non-static "builder" - a factory. Follow the strategy pattern.
If you already know the type when calling the method, this code can help you :
private String CreateOFSMessage(Class<T> classOrder) {
if ("OpenOrder".equals(classOrder.getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((classOrder) order);
}else if ("ExecutionOrder".equals(classOrder.getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((classOrder) order);
}
}
A user enters a code and the type of that code is determined by regular expressions. There are many different type of codes, such as EAN, ISBN, ISSN and so on. After the type is detected, a custom query has to be created for the code. I thought it might be a good idea to create a strategy for type, but with time it feels wrong.
public interface SearchQueryStrategie {
SearchQuery createSearchQuery(String code);
}
-
public class IssnSearchQueryStrategie implements SearchQueryStrategie {
#Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for issn number
}
}
-
public class IsbnSearchQueryStrategie implements SearchQueryStrategie {
#Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for ISBN number
}
}
-
public class EanShortNumberSearchQueryStrategie implements SearchQueryStrategie {
#Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for ean short number
}
}
-
public class TestApplication {
public static void main(final String... args) {
final String code = "1144875X";
SearchQueryStrategie searchQueryStrategie = null;
if (isIssn(code)) {
searchQueryStrategie = new IssnSearchQueryStrategie();
} else if (isIsbn(code)) {
searchQueryStrategie = new IsbnSearchQueryStrategie();
} else if (isEan(code)) {
searchQueryStrategie = new EanShortNumberSearchQueryStrategie();
}
if (searchQueryStrategie != null) {
performSearch(searchQueryStrategie.createSearchQuery(code));
}
}
private SearchResult performSearch(final SearchQuery searchQuery) {
// perform search
}
// ...
}
I have to say that there are many more strategies. How should I dispatch the code to the right strategy?
My second approach was to put a boolean method into every strategy to decide if the code is correct for that strategy.
public class TestApplication {
final SearchQueryStrategie[] searchQueryStrategies = {new IssnSearchQueryStrategie(), new IsbnSearchQueryStrategie(),
new EanShortNumberSearchQueryStrategie()};
public static void main(final String... args) {
final String code = "1144875X";
for (final SearchQueryStrategie searchQueryStrategie : searchQueryStrategie) {
if (searchQueryStrategie.isRightCode(code)) {
searchQueryStrategie.createSearchQuery(code);
break;
}
}
}
private SearchResult performSearch(final SearchQuery searchQuery) {
// perform search
}
// ...
}
How would you solve this problem? Is the strategy pattern the right one for my purposes?
If you are using Java 8 and you can profit from the functional features I think one Enum will be sufficient.
You can avoid using if/else statements by mapping each type of code with a Function that will return the query that needs to be executed:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
public enum CodeType
{
EAN("1|2|3"),
ISBN("4|5|6"),
ISSN("7|8|9");
String regex;
Pattern pattern;
CodeType(String regex)
{
this.regex = regex;
this.pattern = Pattern.compile(regex);
}
private static Map<CodeType, Function<String, String>> QUERIES =
new HashMap<>();
static
{
QUERIES.put(EAN, (String code) -> String.format("Select %s from EAN", code));
QUERIES.put(ISBN, (String code) -> String.format("Select %s from ISBB", code));
QUERIES.put(ISSN, (String code) -> String.format("Select %s from ISSN", code));
}
private static CodeType evalType(String code)
{
for(CodeType codeType : CodeType.values())
{
if (codeType.pattern.matcher(code).matches())
return codeType;
}
// TODO DON'T FORGET ABOUT THIS NULL HERE
return null;
}
public static String getSelect(String code)
{
Function<String, String> function = QUERIES.get(evalType(code));
return function.apply(code);
}
}
And in the main you can test your query:
public class Main
{
public static void main(String... args)
{
System.out.println(CodeType.getSelect("1"));
// System.out: Select 1 from EAN
System.out.println(CodeType.getSelect("4"));
// System.out: Select 4 from ISBB
System.out.println(CodeType.getSelect("9"));
// System.out: Select 9 from ISSN
}
}
I usually tend to keep the code as compact as possible.
Some people dislike enums, so I believe you can use a normal class instead.
You can engineer further the way you obtain the QUERIES (selects), so instead of having String templates you can have a Runnable there.
If you don't want to use the the functional aspects of Java 8 you can use Strategy objects that are associated with each type of code:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
public enum CodeType2
{
EAN("1|2|3", new StrategyEAN()),
ISBN("4|5|6", new StrategyISBN()),
ISSN("7|8|9", new StrategyISSN());
String regex;
Pattern pattern;
Strategy strategy;
CodeType2(String regex, Strategy strategy)
{
this.regex = regex;
this.pattern = Pattern.compile(regex);
this.strategy = strategy;
}
private static CodeType2 evalType(String code)
{
for(CodeType2 codeType2 : CodeType2.values())
{
if (codeType2.pattern.matcher(code).matches())
return codeType2;
}
// TODO DON'T FORGET ABOUT THIS NULL HERE
return null;
}
public static void doQuery(String code)
{
evalType(code).strategy.doQuery(code);
}
}
interface Strategy { void doQuery(String code); }
class StrategyEAN implements Strategy {
#Override
public void doQuery(String code)
{
System.out.println("EAN-" + code);
}
}
class StrategyISBN implements Strategy
{
#Override
public void doQuery(String code)
{
System.out.println("ISBN-" + code);
}
}
class StrategyISSN implements Strategy
{
#Override
public void doQuery(String code)
{
System.out.println("ISSN-" + code);
}
}
And the main method will look like this:
public class Main
{
public static void main(String... args)
{
CodeType2.doQuery("1");
CodeType2.doQuery("4");
CodeType2.doQuery("9");
}
}
So, The strategy pattern is indeed the right choice here, but strategy by itself is not enough. You have several options:
Use a Factory with simple if/else or switch. It's ugly, error prone to extend with new strategies, but is simple and quick to implement.
Use a registry. During the application initialization phase you can register in a registry each SearchQueryStratgeyFactory with the right code. For instance if you use a simple Map you can just do :
strategyRegistry.put("isbn", new IsbnSearchStrategyFactory());
strategyRegistry.put("ean", new EanSearchStrategyFactory());
.... and so on
Then when you need to get the right strategy you just get() the strategy factory from the map using the code id. This approach is better if you have a lot of strategies, but it requires an aditional iitialization step during the application startup.
Use a service locator. ServiceLocator is a pattern that enables the dynamic lookup of implementations. Java comes with an implementation of the ServiceLocator pattern -> the infamous ServiceLoader class. This is my favourite approach because it allows for complete decoupling of the consumer and implementation. Also using the service locator you can easily add new strategies without having to modify the existing code. I won't explain how to use the ServiceLoader - there is plenty of information online. I'll just mention that using the service locator you'll need to implement a "can process such codes ?" logic in each strategy factory. For instance if the factory cannot create a strategy for "isbn" then return null and try with the next factory.
Also note that in all cases you work with factories that produce the strategy implementations.
PS: It's strategy not strategie :)
Your approach is not the Strategy Pattern. Strategy Pattern is all about customizing behavior of an object (Context in terms of this pattern) by passing alternative Strategy object to it. By this way, we don't need to modify the source code of the Context class but still can customize the behavior of objects instanced from it.
Your problem is somewhat related to the Chain of Responsibility (CoR) Pattern where you have a request (your code) and need to figure out which SearchQueryStrategie in a predefined list should handle the request.
The second approach -- using array -- that you mentioned is fine. However, to make it usable in production code, you must have another object -- let's say Manager -- that manages the array and is responsible to find the relevant element for each request. So your client code have to depend on two objects: the Manager and the result SearchQueryStrategie. As you can see, the source code of Manager class tend to be changed frequently because new implementations of SearchQueryStrategie may come. This might make your client annoyed.
That's why the CoR Pattern uses the linked list mechanism instead of array. Each SearchQueryStrategie object A would hold a reference to a next SearchQueryStrategie B. If A cannot handle the request, it will delegate to B (it can even decorate the request before delegating). Of course, somewhere still must know all kinds of strategies and create a linked list of SearchQueryStrategie, but your client will then depend only on a SearchQueryStrategie object (the head one of the list).
Here is the code example:
class SearchQueryConsumer {
public void consume(SearchQuery sq) {
// ...
}
}
abstract class SearchQueryHandler {
protected SearchQueryHandler next = null;
public void setNext(SearchQueryHandler next) { this.next = next; }
public abstract void handle(String code, SearchQueryConsumer consumer);
}
class IssnSearchQueryHandler extends SearchQueryHandler {
#Override
public void handle(String code, SearchQueryConsumer consumer) {
if (issn(code)) {
consumer.consume(/* create a SearchQuery */);
} else if (next != null) {
next.handle(code, consumer);
}
}
private boolean issn(String code) { ... }
}
What i recommend is using the Factory pattern. It describes and handles your scenario better.
Factory Pattern
You can design in the following way (using concepts of factory DP and polymorphism):
Code as interface.
ISSNCode, ISBNCode and EANCode as concrete classes
implementing Code interface, having single-arg constructor taking text as String.
Code has method getInstanceOfCodeType(String text) which returns an instance of a sub-class of Code (decided by checking the type of text passed to it). Let's say the returned value be code
Class SearchQueryStrategieFactory with
getSearchQueryStrategie(code) method. It consumes the returned value from step 3, and generates different
instances of SearchQueryStrategie subclasses based on code type using new operator and, then returns the same.
So, you need to call two methods getInstanceOfCodeType(text) and getSearchQueryStrategie(code) from anywhere.
Instead of implicitly implementing the factory inside main, keep the whole factory code separate, to make it easily maintainable and extensible .
In one of my projects I need to compare the URI with several regex patterns(15+ regex patterns). Currently I have used a if ladder to see if either one of them gets matched and there onward the logical part of the code is executed.
Glimpse of the code now:
if (uri.matches(Constants.GET_ALL_APIS_STORE_REGEX)) {
long lastUpdatedTime = InBoundETagManager.apisGet(null, null, tenantDomain, null);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
if (eTag.equals(ifNoneMatch)) {
message.getExchange().put("ETag", eTag);
generate304NotModifiedResponse(message);
}
message.getExchange().put("ETag", eTag);
}
else if (uri.matches(Constants.GET_API_FOR_ID_REGEX)) { // /apis/{apiId}
apiId = UUIDList.get(0);
String requestedTenantDomain = RestApiUtil.getRequestedTenantDomain(tenantDomain);
long lastUpdatedTime = InBoundETagManager.apisApiIdGet(apiId, requestedTenantDomain, uri);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
handleInterceptorResponse(message, ifNoneMatch, eTag);
}
else if (uri.matches(Constants.GET_SWAGGER_FOR_API_ID_REGEX)) { // /apis/{apiId}/swagger
apiId = UUIDList.get(0);
long lastUpdatedTime = InBoundETagManager.apisApiIdSwaggerGet(apiId, tenantDomain);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
if (lastUpdatedTime == 0L) {
log.info("No last updated time available for the desired API swagger json file");
}
handleInterceptorResponse(message, ifNoneMatch, eTag);
}
Can someone please introduce me with a more neat and clever way of doing this regex matching thing?
One url-type(regex) = one handler = one class. This way would be much easier to read and support especially if you have 15 regex checks.
interface URLHandler {
void handle();
boolean isSupported(String url);
}
class GetAllApisStoreHandler implements URLHandler{
private static final Pattern GET_ALL_API_STORE_PATTERN = Pattern.compile(GET_ALL_APIS_STORE_REGEX);
public boolean isSupported(String url) {
return GET_ALL_API_STORE_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class GetApiIdHandler implements URLHandler{
private static final Pattern GET_API_ID_REGEX = Pattern.compile(GET_API_ID_REGEX);
public boolean isSupported(String url) {
return GET_API_ID_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class GetApiIdHandler implements URLHandler{
private static final Pattern GET_SWAGGER_FORAPI_ID_PATTERN = Pattern.compile(GET_SWAGGER_FOR_API_ID_REGEX);
public boolean isSupported(String url) {
return GET_SWAGGER_FORAPI_ID_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class Main {
private List<URLHandler> urlHandlers;
public void method(){
...
for (URLHandler handler : urlHandlers) {
if(handler.isSupported(url)) {
handler.handle(arg1, arg2, arg3, ...);
}
}
...
}
}
Using multiple classes as #KonstantinLabun proposed is probably the way to go(*), but it shouldn't lead to much code duplication. So use an abstract class instead of (or in addition to an interface). Or (mis)use default methods.
abstract class URLHandler {
abstract void handle();
abstract Pattern urlPattern():
final boolean isSupported(String url) {
return urlPattern().matcher(url).matches();
}
}
class GetAllApisStoreHandler extends URLHandler{
private static final Pattern URL_PATTERN =
Pattern.compile(Constants.GET_ALL_APIS_STORE_REGEX);
Pattern urlPattern() {
return URL_PATTERN;
}
public void handle(...) {
...
}
}
There's no need to invent names for the PATTERN as its scope identified it already. The static field exists only as an optimization, so that the Pattern don't get compiled for each match.
(*) There's nothing wrong with a single class, as long as it's concise (I like spaghetti except in code) and doesn't leak implementation details. There's nothing wrong with multiple classes (except maybe on Android as 50 kB per class might matter) as long as they don't lead to code bloat. An enum is sometimes a good solution, too.
Explanation of abstract class vs. interface
An interface forces you to implement its methods(**), which may quickly lead to duplication. It's advantage is multiple inheritance and some conceptual purity.
An abstract class allows you to gather the common parts. But there's no dilemma, you can do both, see e.g., interface List and abstract class AbstractList.
(**) Since Java 8, an interface can have default methods, so this is no more true. Assuming you want to use them for this purpose. It can't declare any state, but it can access the state of the object. For example, my above URLHandler could be such an interface. There are still disadvantages, e.g., methods must be public and mustn't be final.
Does anyone know a library or some at least some research on creating and using persistent data structures in Java? I don't refer to persistence as long term storage but persistence in terms of immutability (see Wikipedia entry).
I'm currently exploring different ways to model an api for persistent structures. Using builders seems to be a interesting solution:
// create persistent instance
Person p = Builder.create(Person.class)
.withName("Joe")
.withAddress(Builder.create(Address.class)
.withCity("paris")
.build())
.build();
// change persistent instance, i.e. create a new one
Person p2 = Builder.update(p).withName("Jack");
Person p3 = Builder.update(p)
.withAddress(Builder.update(p.address())
.withCity("Berlin")
.build)
.build();
But this still feels somewhat boilerplated. Any ideas?
Builders will make your code too verbose to be usable. In practice, almost all immutable data structures I've seen pass in state through the constructor. For what its worth, here are a nice series of posts describing immutable data structures in C# (which should convert readily into Java):
Part 1: Kinds of Immutability
Part 2: Simple Immutable Stack
Part 3: Covariant Immutable Stack
Part 4: Immutable Queue
Part 5: Lolz! (included for completeness)
Part 6: Simple Binary Tree
Part 7: More on Binary Trees
Part 8: Even More on Binary Trees
Part 9: AVL Tree Implementation
Part 10: Double-ended Queue
Part 11: Working Double-ended Queue Implementation
C# and Java are extremely verbose, so the code in these articles is quite scary. I recommend learning OCaml, F#, or Scala and familiarizing yourself with immutability with those languages. Once you master the technique, you'll be able to apply the same coding style to Java much more easily.
I guess the obvious choices are:
o Switch to a transient data structure (builder) for the update. This is quite normal. StringBuilder for String manipulation for example. As your example.
Person p3 =
Builder.update(p)
.withAddress(
Builder.update(p.address())
.withCity("Berlin")
.build()
)
.build();
o Always use persistent structures. Although there appears to be lots of copying, you should actually be sharing almost all state, so it is nowhere near as bad as it looks.
final Person p3 = p
.withAddress(
p.address().withCity("Berlin")
);
o Explode the data structure into lots of variables and recombine with one huge and confusing constructor.
final Person p3 = Person.of(
p.name(),
Address.of(
p.house(), p.street(), "Berlin", p.country()
),
p.x(),
p.y(),
p.z()
);
o Use call back interfaces to provide the new data. Even more boilerplate.
final Person p3 = Person.of(new PersonInfo(
public String name () { return p.name(); )
public Address address() { return Address.of(new AddressInfo() {
private final Address a = p.address();
public String house () { return a.house() ; }
public String street () { return a.street() ; }
public String city () { return "Berlin" ; }
public String country() { return a.country(); }
})),
public Xxx x() { return p.x(); }
public Yyy y() { return p.y(); }
public Zzz z() { return p.z(); }
});
o Use nasty hacks to make fields transiently available to code.
final Person p3 = new PersonExploder(p) {{
a = new AddressExploder(a) {{
city = "Berlin";
}}.get();
}}.get();
(Funnily enough I was just put down a copy of Purely Functional Data Structures by Chris Okasaki.)
Have a look at Functional Java. Currently provided persistent datastructures include:
Singly-linked list (fj.data.List)
Lazy singly-linked list (fj.data.Stream)
Nonempty list (fj.data.NonEmptyList)
Optional value (a container of length 0 or 1) (fj.data.Option)
Set (fj.data.Set)
Multi-way tree (a.k.a. rose tree) (fj.data.Tree)
Immutable map (fj.data.TreeMap)
Products (tuples) of arity 1-8 (fj.P1..P8)
Vectors of arity 2-8 (fj.data.vector.V2..V8)
Pointed list (fj.data.Zipper)
Pointed tree (fj.data.TreeZipper)
Type-safe, generic heterogeneous list (fj.data.hlist.HList)
Immutable arrays (fj.data.Array)
Disjoint union datatype (fj.data.Either)
A number of usage examples are provided with the binary distribution. The source is available under a BSD license from Google Code.
I implemented a few persistent data structures in Java. All open source (GPL) on Google code for anyone who is interested:
http://code.google.com/p/mikeralib/source/browse/#svn/trunk/Mikera/src/mikera/persistent
The main ones I have so far are:
Persistent mutable test object
Persistent hash maps
Persistent vectors/lists
Persistent sets (including a specialised persistent set of ints)
Follow a very simple tentative with dynamic proxy:
class ImmutableBuilder {
static <T> T of(Immutable immutable) {
Class<?> targetClass = immutable.getTargetClass();
return (T) Proxy.newProxyInstance(targetClass.getClassLoader(),
new Class<?>[]{targetClass},
immutable);
}
public static <T> T of(Class<T> aClass) {
return of(new Immutable(aClass, new HashMap<String, Object>()));
}
}
class Immutable implements InvocationHandler {
private final Class<?> targetClass;
private final Map<String, Object> fields;
public Immutable(Class<?> aTargetClass, Map<String, Object> immutableFields) {
targetClass = aTargetClass;
fields = immutableFields;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("toString")) {
// XXX: toString() result can be cached
return fields.toString();
}
if (method.getName().equals("hashCode")) {
// XXX: hashCode() result can be cached
return fields.hashCode();
}
// XXX: naming policy here
String fieldName = method.getName();
if (method.getReturnType().equals(targetClass)) {
Map<String, Object> newFields = new HashMap<String, Object>(fields);
newFields.put(fieldName, args[0]);
return ImmutableBuilder.of(new Immutable(targetClass, newFields));
} else {
return fields.get(fieldName);
}
}
public Class<?> getTargetClass() {
return targetClass;
}
}
usage:
interface Person {
String name();
Person name(String name);
int age();
Person age(int age);
}
public class Main {
public static void main(String[] args) {
Person mark = ImmutableBuilder.of(Person.class).name("mark").age(32);
Person john = mark.name("john").age(24);
System.out.println(mark);
System.out.println(john);
}
}
grow directions:
naming policy (getName, withName, name)
caching toString(), hashCode()
equals() implementations should be straightforward (although not implemented)
hope it helps :)
It is very difficult, if not impossible, to make things immutable that ain't designed so.
If you can design from ground up:
use only final fields
do not reference non immutable objects
Do you want immutability :
so external code cannot change the data?
so once set a value cannot be changed?
In both cases there are easier ways to accomplish the desired result.
Stopping external code from changing the data is easy with interfaces:
public interface Person {
String getName();
Address getAddress();
}
public interface PersonImplementor extends Person {
void setName(String name);
void setAddress(Address address);
}
public interface Address {
String getCity();
}
public interface AddressImplementor {
void setCity(String city);
}
Then to stop changes to a value once set is also "easy" using java.util.concurrent.atomic.AtomicReference (although hibernate or some other persistence layer usage may need to be modified):
class PersonImpl implements PersonImplementor {
private AtomicReference<String> name;
private AtomicReference<Address> address;
public void setName(String name) {
if ( !this.name.compareAndSet(name, name)
&& !this.name.compareAndSet(null, name)) {
throw new IllegalStateException("name already set to "+this.name.get()+" cannot set to "+name);
}
}
// .. similar code follows....
}
But why do you need anything more than just interfaces to accomplish the task?
Google Guava now hosts a variety of immutable/persistent data structures.