Java Design: How to expose and encapsulate a vendor API? - java

I know basic Java, but I struggle sometimes with object orientation design.
There is a vendor api I use, and I wanted to wrap it to be reusable as a lib in other projects.
All the services from the vendor are different classes and have no hierarchy and so on, but I have no option to change it.
So I want to use composition and ensure I don't repeat myself.
I thought initially to create a service that would receive the parameters that are common to all services, and this service would implement the api.
I tried refactoring this code here and there, and I'm pretty sure this design I'm trying has some great problems as I noticed when trying to create unit tests :)
How could I achieve a better design?
Code as of now:
/* This is how I call the service from the vendor today */
class VendorConsumer {
void exampleCake() {
VendorServiceCake vendorServiceCake = new VendorServiceCake();
VendorApiCake cakeApi = a.getCakeApi(1234);
cakeApi.authenticate("user", "password");
cakeApi.cookDeliciousCake(CakeIngredients ingredients);
}
void exampleSellPie() {
VendorServiceSellPie vendorServiceSellPie = new VendorServiceSellPie();
VendorApiSellPie apiSellPie = a.getPieApi(1234); //same parameters as above
apiSellPie.authenticate("user", "password"); //same parameters as above
apiSellPie.sellDeliciousPie(List<Customer> customer);
}
}
// ---------------------------------------------
/* Below is what I'm trying to do */
class UsageTest {
// This is how users of my .jar would call it
void usage() {
BakeryService service = new BakeryServiceCake("user", "password", 1234);
List<Cake> cakeList = service.cookDeliciousCake(CakeIngredients ingrediets);
}
void usage2() {
BakeryService service = new BakeryServiceSellPie("user", "password", 1234);
List<Payments> payments = service.sellDeliciousPie(List<Customer> customer);
}
}
class BakeryService { //is this class useless?
public BakeryService(String user, String pass, int parameterNeeded) {
}
private void checkParameters() {
//do some checkings of the parameters
}
}
class BakeryServiceCake extends BakeryService implements KitchenCakeApi {
KitchenCakeApi api;
public BakeryServiceCake(String user, String pass, int parameterNeeded) {
super(user, pass, parameterNeeded);
this.api = new KitchenCakeApiImpl(user, pass, parameterNeeded)
}
#Override
public void authenticate() {
api.authenticate();
}
#Override
public void cookDeliciousCake(CakeIngredients ingredients) {
api.cookDeliciousCake(ingredients);
}
}
interface KitchenCakeApi {
void authenticate();
void cookDeliciousCake(CakeIngredients ingredients);
}
class KitchenCakeApiImpl implements KitchenCakeApi {
private VendorServiceCake vendorServiceCake;
private VendorApiCake cakeApi;
public KitchenCakeApiImpl(String user, String pass, int parameterNeeded) {
vendorServiceCake = new VendorServiceCake();
cakeApi = a.getCakeApi(parameterNeeded); // that 1234
}
#Override
public void authenticate() {
cakeApi.authenticate("user", "password");
}
#Override
public void cookDeliciousCake(CakeIngredients ingredients) {
cakeApi.cookDeliciousCake(CakeIngredients ingredients);
}
}
Thanks!

The API's generally have some hierarchy/structure to it but you will need a keen eye to observe it, let's just say Java File handling is based on decorator pattern that too almost exact text book implementation.
Hard to answer without seeing the vendor API.
Probable Approach -
Use Adapter pattern help to encapsulates you from third party API.
Adapter has a cost to it which is lots of code to write but maintenance is easier.
If Adapters explode use it with proxy pattern to make a life bit easier.
Facade can help you in case of complex operations but this could be just an add-on in your case.
Two cents - http://www.oodesign.com/ go through design patterns just as an overview and see the problem it solves if it rings a bell go deeper else move on.
Happy designing.

Related

Design pattern for state-based entity handling

My question is about what should be the most OOP solution and the right design pattern for my situation. We have a user entity and multiple account entities belong to the user. Account entities can have multiple states and we can execute multiple operations on accounts. The outcome of these operations is based on the account entity's state.
I have the following code which is based mostly on switch (sometimes it looks like a few "if"). I would like to change it but cannot find the right design pattern.
enum Status {
ACTIVE, INACTIVE, DELETED;
}
#Entity
class Account {
private long id;
private long userid;
private Status status;
//...
}
class AccountService{
Account delete(long id) {
//...
if (accountInfo.getSatus() == DELETED) {
throw new IllegalOperationException();
}
if (accountInfo.getStatus() == ACTIVE || accountInfo.getStatus()) {
accountInfo.setStatus(DELETED);
accountInfoRepository.save(accountInfo);
}
}
Account create (Account account) {
// various operations based on state
}
}
I really want to refactor these codes, I fear that as soon as our service grows it will contain more "magic" and will be hard to maintain. And if we would like to introduce a new state it will be nearly impossible.
My junior mind thought that I should have state objects which would implement all the operations, in pseudo-code style:
class AccountService {
private StateFactory stateFactory;
private AccountRepository accountRepository;
Account delete(long id) {
final Optional<Account> account = accountRepository.findById(id);
Account deletedAccount = account.map(stateFactory::getByState)
.map(accountState -> accountState.delete(account))
.orElseThrow(() -> new IllegalOperationException());
return accountRepository.save(deletedAccount);
}
Account create (Account account) {
// various operation based on state
}
}
and:
class ActiveState extends AccountState {
#Override
public Account delete(Account account) {
//implementation
}
#Override
public Account activate(AccountInfo) {
// implementation
}
}
and:
interface AccountState {
Account activate(AccountInfo);
Account delete(AccountInfo);
}
I know there must be a better implementation for this problem. Which other design patterns are suitable for this setup?
UPDATE
I have found a few interesting articles to read in the topic:
How to implement a FSM - Finite State Machine in Java
When you have more complex state handling
If I understood question correctly, then it is necessary to apply some action by its state.
If it is true, then we can use Factory pattern to get desired object which can execute some action. Mapping between state and action can be putted into HashTable.
So let's see an example of code. I will write via C#, but this code can be easily translated to Java because languages have many syntax similarities.
So we will have enum of statuses:
public enum Status
{
Active,
Deleted,
Foo
}
and states of AccountState
public abstract class AccountState
{
public abstract void ExecSomeLogic();
}
public class ActiveState : AccountState // "extends" instead of ":" in Java
{
public override void ExecSomeLogic()
{
}
}
public class DeletedState : AccountState // "extends" instead of ":" in Java
{
public override void ExecSomeLogic()
{
}
}
public class FooState : AccountState // "extends" instead of ":" in Java
{
public override void ExecSomeLogic()
{
}
}
Then we need mapper class of Status to their AccountState:
public class StatusToAccountState
{
public Dictionary<Status, AccountState> AccountStateByStatus { get; set; } =
new Dictionary<Status, AccountState>() // HashMap in Java
{
{ Status.Active, new ActiveState() },
{ Status.Deleted, new DeletedState() },
{ Status.Foo, new FooState() },
};
}
And then in your service you can use it like this:
void Delete(long id, Status status)
{
StatusToAccountState statusToAccountState = new StatusToAccountState();
AccountState accountState = statusToAccountState.AccountStateByStatus[status];
accountState.ExecSomeLogic();
}
If there are many logic to figure out what Status of object is, then you can create some class which will have just one responisibility of figuring out what state of object is:
public class StatusManager
{
public Status Get()
{
return Status.Active; // or something another based on logic
}
}
After doing this, your classes will correspond to the single responsibility principle of SOLID. Read more about single responsibility principle of SOLID here
Too many switch-/if-Statements indicate the code smell "Tool Abusers" (see M. Fowler "Refactoring"). Use the polymorphism mechanics to solve this.
https://refactoring.guru/smells/switch-statements

Strategy Pattern too many if statements

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 .

Design the interaction between classes - regarding the Solution 8.7 of Cracking the Coding Interview

I have learning the book Cracking the Coding Interview, and having some questions about the Chapter 8: Object-Oriented Design. Take the question 8.7 as an example:
8.7 Explain how you would design a chat server. In particular, provide details about the various backend components, classes, and methods. What would be the hardest problems to solve?
The solution can be found this github repo.
My general question is: how to assign different methods to different classes in order to complete a functionality? Is there some principles or common ideas of making these interactions? Take one specific functionality - User A adds User B - as an example:
The following is partial code of UserManager:
public class UserManager {
...
public void addUser(User fromUser, String toAccountName) {
User toUser = usersByAccountName.get(toAccountName);
AddRequest req = new AddRequest(fromUser, toUser, new Date());
toUser.receivedAddRequest(req);
fromUser.sentAddRequest(req);
}
public void approveAddRequest(AddRequest req) {
req.status = RequestStatus.Accepted;
User from = req.getFromUser();
User to = req.getToUser();
from.addContact(to);
to.addContact(from);
}
public void rejectAddRequest(AddRequest req) {
req.status = RequestStatus.Rejected;
User from = req.getFromUser();
User to = req.getToUser();
from.removeAddRequest(req);
to.removeAddRequest(req);
}
...
}
The following is partial code of User:
public class User {
...
public boolean addContact(User user) {
if (contacts.containsKey(user.getId())) {
return false;
} else {
contacts.put(user.getId(), user);
return true;
}
}
public void receivedAddRequest(AddRequest req) {
int senderId = req.getFromUser().getId();
if (!receivedAddRequests.containsKey(senderId)) {
receivedAddRequests.put(senderId, req);
}
}
public void sentAddRequest(AddRequest req) {
int receiverId = req.getFromUser().getId();
if (!sentAddRequests.containsKey(receiverId)) {
sentAddRequests.put(receiverId, req);
}
}
public void removeAddRequest(AddRequest req) {
if (req.getToUser() == this) {
receivedAddRequests.remove(req);
} else if (req.getFromUser() == this) {
sentAddRequests.remove(req);
}
}
public void requestAddUser(String accountName) {
UserManager.getInstance().addUser(this, accountName);
}
...
}
I am just wondering where approveAddRequest(AddRequest req) and rejectAddRequest(AddRequest req) are called. After AddRequest is sent or received, no further behaviors will be done for really process this request.
And also, I am wondering: should every interaction between class be like this (i.e., lots of methods from different classes are calling and called many times)?
What you're looking for is "Observer" Pattern.
It's best-practice introduced by GoF for event handling.
What you've presented here has nothing to do with chat. It's just utility for dealing with user interaction.
As for you last question: you're not seeing full code, so you can't know where it's called from. You should find full source code first. And you're right, with presented code no behaviour would be done, but for real chat there should be. This whole things is a mock.

Is there a good design pattern for checking permissions when running a method?

I'm looking for a design pattern or a approach to tidy up my code. This is in the context of security and checking permissions for a given subject.
Here is a simplified example:
public Pizza bakePizza() throws UnauthorizedException{
if (subject.isPermitted("BAKE_PIZZA")){
return new Pizza();
} else {
throw new UnauthorizedException();
}
}
Is there a way to make this a bit more clean because this can get quite messy when I have a lot of different kinds of methods like this.
I think splitting security constraints and bussines logic by using something like the decorator pattern would be a good start.
How about something like this:
// an interface defines the operations
public interface PizzaService {
Pizza bakePizza();
}
// standard implementation that contains the business logic
public class PizzaServiceImpl implements PizzaService {
#Override
public Pizza bakePizza() {
// implementation ..
}
}
// implementation with security constraints that delegates to the real implementation
public class SecurePizzaService implements PizzaService {
private PizzaService service;
public SecurePizzaService(PizzaService service) {
this.service = service;
}
#Override
public Pizza bakePizza() {
if (!subject.isPermitted("BAKE_PIZZA")){
throw new UnauthorizedException();
}
service.bakePizza()
}
}
// usage
PizzaService pizzaService = new SecurePizzaService(new PizzaServiceImpl());
...
Pizza pizza = pizzaService.bakePizza();
This way you could change security constraints without touching business logic and vice versa.
If you have a lot of situations like this you should have a look at AOP frameworks like AspectJ
One approach is to move the check logic into a method that throws and thereby ends the program flow of the calling method early.
public Pizza bakePizza() throws UnauthorizedException{
ensurePermission("BAKE_PIZZA");
return new Pizza();
}
private void ensurePermission(String permission) throws UnauthorizedException {
if (!subject.isPermitted(permission))
throw new UnauthorizedException();
}

fuzzy implementation for capturing specific strings

I am going to develop a web crawler using java to capture hotel room prices from hotel websites.
In this case I want to capture room price with the room type and the meal type, so my algorithm should be intelligent to handle that.
For example:
Room type: Deluxe
Meal type: HalfBoad
price : $20.00
The main problem is room prices can be in different ways in different hotel sites. So my algorithm should be independent from hotel sites.
I am plan to use above room types and meal types as a fuzzy sets and compare the words in webpage with above fuzzy sets using a suitable membership function.
Anyone experienced with this? or have an idea for my problem?
There are two ways to approach this problem:
You can customize your crawler to understand the formats used by different Websites; or
You can come up with a general ("fuzzy") solution.
(1) will, by far, be the easiest. Ideally you want to create some tools that make this easier so you can create a filter for any new site in minimal time. IMHO your time will be best spent with this approach.
(2) has lots of problems. Firstly it will be unreliable. You will come across formats you don't understand or (worse) get wrong. Second, it will require a substantial amount of development to get something working. This is the sort of thing you use when you're dealing with thousands or millions of sites.
With hundreds of sites you will get better and more predictable results with (1).
As with all problems, design can let you deliver value adapt to situations you haven't considered much more quickly than the general solution.
Start by writing something that parses the data from one provider - the one with the simplest format to handle. Find a way to adapt that handler into your crawler. Be sure to encapsulate construction - you should always do this anyway...
public class RoomTypeExtractor
{
private RoomTypeExtractor() { }
public static RoomTypeExtractor GetInstance()
{
return new RoomTypeExtractor();
}
public string GetRoomType(string content)
{
// BEHAVIOR #1
}
}
The GetInstance() ,ethod lets you promote to a Strategy pattern for practically free.
Then add your second provider type. Say, for instance, that you have a slightly more complex data format which is a little more prevalent than the first format. Start by refactoring what was your concrete room type extractor class into an abstraction with a single variation behind it and have the GetInstance() method return an instance of the concrete type:
public abstract class RoomTypeExtractor
{
public static RoomTypeExtractor GetInstance()
{
return SimpleRoomTypeExtractor.GetInstance();
}
public abstract string GetRoomType(string content);
}
public final class SimpleRoomTypeExtractor extends RoomTypeExtractor
{
private SimpleRoomTypeExtractor() { }
public static SimpleRoomTypeExtractor GetInstance()
{
return new SimpleRoomTypeExtractor();
}
public string GetRoomType(string content)
{
// BEHAVIOR #1
}
}
Create another variation that implements the Null Object pattern...
public class NullRoomTypeExtractor extends RoomTypeExtractor
{
private NullRoomTypeExtractor() { }
public static NullRoomTypeExtractor GetInstance()
{
return new NullRoomTypeExtractor();
}
public string GetRoomType(string content)
{
// whatever "no content" behavior you want... I chose returning null
return null;
}
}
Add a base class that will make it easier to work with the Chain of Responsibility pattern that is in this problem:
public abstract class ChainLinkRoomTypeExtractor extends RoomTypeExtractor
{
private final RoomTypeExtractor next_;
protected ChainLinkRoomTypeExtractor(RoomTypeExtractor next)
{
next_ = next;
}
public final string GetRoomType(string content)
{
if (CanHandleContent(content))
{
return GetRoomTypeFromUnderstoodFormat(content);
}
else
{
return next_.GetRoomType(content);
}
}
protected abstract bool CanHandleContent(string content);
protected abstract string GetRoomTypeFromUnderstoodFormat(string content);
}
Now, refactor the original implementation to have a base class that joins it into a Chain of Responsibility...
public final class SimpleRoomTypeExtractor extends ChainLinkRoomTypeExtractor
{
private SimpleRoomTypeExtractor(RoomTypeExtractor next)
{
super(next);
}
public static SimpleRoomTypeExtractor GetInstance(RoomTypeExtractor next)
{
return new SimpleRoomTypeExtractor(next);
}
protected string CanHandleContent(string content)
{
// return whether or not content contains the right format
}
protected string GetRoomTypeFromUnderstoodFormat(string content)
{
// BEHAVIOR #1
}
}
Be sure to update RoomTypeExtractor.GetInstance():
public static RoomTypeExtractor GetInstance()
{
RoomTypeExtractor extractor = NullRoomTypeExtractor.GetInstance();
extractor = SimpleRoomTypeExtractor.GetInstance(extractor);
return extractor;
}
Once that's done, create a new link for the Chain of Responsibility...
public final class MoreComplexRoomTypeExtractor extends ChainLinkRoomTypeExtractor
{
private MoreComplexRoomTypeExtractor(RoomTypeExtractor next)
{
super(next);
}
public static MoreComplexRoomTypeExtractor GetInstance(RoomTypeExtractor next)
{
return new MoreComplexRoomTypeExtractor(next);
}
protected string CanHandleContent(string content)
{
// Check for presence of format #2
}
protected string GetRoomTypeFromUnderstoodFormat(string content)
{
// BEHAVIOR #2
}
}
Finally, add the new link to the chain, if this is a more common format, you might want to give it higher priority by putting it higher in the chain (the real forces that govern the order of the chain will become apparent when you do this):
public static RoomTypeExtractor GetInstance()
{
RoomTypeExtractor extractor = NullRoomTypeExtractor.GetInstance();
extractor = SimpleRoomTypeExtractor.GetInstance(extractor);
extractor = MoreComplexRoomTypeExtractor.GetInstance(extractor);
return extractor;
}
As time passes, you may want to add ways to dynamically add new links to the Chain of Responsibility, as pointed out by Cletus, but the fundamental principle here is Emergent Design. Start with high quality. Keep quality high. Drive with tests. Do those three things and you will be able to use the fuzzy logic engine between your ears to overcome almost any problem...
EDIT
Translated to Java. Hope I did that right; I'm a little rusty.

Categories