I want to use visitor pattern to extend functionality of classes from external library. The main problem that I can't add any new code to that library. Let's consider simple example.
External elements, which require functionality extension will be represented as tools for home work
public interface Tool {
String name();
}
public record Saw(String name, String purpose) implements Tool {}
public record Screwdriver(String name, String nozzle) implements Tool {}
public record Wrench(String name, int size) implements Tool {}
And new functionality, which I want to add will be represented as workers
public interface Worker<T extends Tool> {
Class<T> getToolClass();
void doWork(T tool);
}
Saw worker
public class SawWorker implements Worker<Saw> {
#Override
public Class<Saw> getToolClass() {
return Saw.class;
}
#Override
public void doWork(Saw tool) {
System.out.println("Sawing with " + tool);
}
}
Screwdriver worker
public class ScrewdriverWorker implements Worker<Screwdriver> {
#Override
public Class<Screwdriver> getToolClass() {
return Screwdriver.class;
}
#Override
public void doWork(Screwdriver tool) {
System.out.println("Tightens screw with " + tool);
}
}
Wrench worker
public class WrenchWorker implements Worker<Wrench> {
#Override
public Class<Wrench> getToolClass() {
return Wrench.class;
}
#Override
public void doWork(Wrench tool) {
System.out.println("Tightens bolt with " + tool);
}
}
And main class to start the show
public class Main {
public static void main(String[] args) {
List<Tool> tools = List.of(
new Saw("hand saw", "for gardener"),
new Saw("chainsaw", "for forester"),
new Screwdriver("torque screwdriver", "line"),
new Screwdriver("powered screwdriver", "cross"),
new Wrench("open-end wrench", 12),
new Wrench("combination wrench", 32));
Map<Class<? extends Tool>, Worker> workers = Stream.of(
new SawWorker(),
new ScrewdriverWorker(),
new WrenchWorker()
).collect(Collectors.toMap(Worker::getToolClass, Function.identity()));
tools.forEach(tool -> workers.get(tool.getClass()).doWork(tool));
}
}
What do you think about such implementation of the pattern? Is it possible to upgrade this solution with use of java8 lambdas? And will it look easier and clearer?
Related
Recently I came across a problem which I was asked to design using appropriate design patterns. The proble statement is:
Implement a remote control of TV.
Any remote control we use, either at home/hotel or at a friend’s place,
we just pick up the TV remote control and start pressing Up and Down
or Forward and Back keys to change the channels.
Choose a suitable design pattern for implementation of this problem.
I am not able to figure out how to design this ask. This is what I came up with:
Place is an abstract class.
Home extends Place
Hotel extends Place
FriendPlace extends Place
TVRemote is a class
Place has a TVRemote
Keys is an interface
Keys has a method press()
UpKey, DownKey, ForwardKey, BackKey are classes implementing Keys
TVRemote has Keys
There could be more Keys in TVRemote
This is what I could think of but unable to incorporate a Design Pattern here. Any guidance?
A simplistic approach will be to create an interface
interface RemoteControl
{
public void up();
public vois down();
public void forward();
public void back();
}
and then create specific classes that will implement that interface for specific devices
e.g.
public class HomeRemote implements RemoteControl {
public void up(){
..
}
public vois down(){
..
}
public void forward(){
..
}
public void back(){
..
}
}
However
After our discussion - and after searching a little bit more, i am inclined to think now that Bridge pattern is what is asked for here.
Check this out - http://www.programcreek.com/2011/10/java-design-pattern-bridge/
There abstract class for remote control is used with basic implementation of (up,down,forward,back)
Then each specific TVRemote extends the abstract class to add more/and device specific functionality.
Also note that TVs are using common interface where (goUp(),goDown(),goForward(),goBack() and possibly on(),off()) functions are described.
Some observations:
different remote controls may have different number of buttons
different buttons execute different actions
remote control should be oblivious of the details how the action is executed
one should be able to reprogram remote control either by assigning different actions to buttons or by supporting different devices
The most straightforward pattern to use with this situation is Command. One could create specific Command implementations and then assign Commands to buttons:
public interface Command {
void Execute();
}
public class Button {
private readonly Command command;
public Button(Command command) {
this.command = command;
}
public void Press() {
this.command.Execute();
}
}
public class Remote {
public Button ButtonPlaceholder1 { get; set; }
public Button ButtonPlaceholder2 { get; set; }
public Button ButtonPlaceholder3 { get; set; }
public Button ButtonPlaceholder4 { get; set; }
}
So, what would be the benefit of having Button class? Well, let's say you want to introduce a slider button, which can be moved Up and Down. In this case, you will configure it with two Commands:
public class SliderButton {
public SliderButton(Command up, Command down) {
this.commandUp = up;
this.commandDown = down;
}
public void Up() {
this.commandUp.Execute();
}
public void Down() {
this.commandDown.Execute();
}
}
And interesting follow-up question on this interview would be, "How to implement a button that would cancel the action made by pressing previous button? (e.g. I was watching ESPN channel, but there was a break in between a match, so I switched to MTV, but I want to check once in a while whether break has ended, and if not, go back to MTV)
You should use command pattern here. Usually it has Invoker, Client, Command and Receiver. Here are the classes you may require.
Command
public interface ICommand {
void execute();
}
Invoker
public class RemoteControl {
Map<Key, ICommand> commandsByKey;
public RemoteControl() {
commandsByKey = new HashMap<>();
}
public void setCommand(Key key, ICommand command) {
commandsByKey.put(key, command);
}
public void press(Key key) throws Exception {
ICommand command = commandsByKey.get(key);
if(command == null)
throw new Exception("Invalid Key");
command.execute();
}
}
Receiver
public class TV {
private String brand;
public TV(String brand) {
this.brand = brand;
}
#Override
public String toString() {
return brand + " TV";
}
}
Client
public abstract class Place {
private TV tv;
private RemoteControl remoteControl;
public Place(TV tv) {
this.tv = tv;
this.remoteControl = new RemoteControl();
remoteControl.setCommand(Key.UP, new UpCommand(this.tv));
remoteControl.setCommand(Key.FORWARD, new ForwardCommand(this.tv));
remoteControl.setCommand(Key.DOWN, new DownCommand(this.tv));
remoteControl.setCommand(Key.BACK, new BackCommand(this.tv));
}
public TV getTv() {
return tv;
}
public RemoteControl getRemoteControl() {
return remoteControl;
}
}
public class Home extends Place {
public Home() {
super(new TV("Sony"));
}
}
public class Hotel extends Place {
public Hotel() {
super(new TV("LG"));
}
}
Concrete Commands
public class UpCommand implements ICommand {
private TV tv;
public UpCommand(TV tv) {
this.tv = tv;
}
#Override
public void execute() {
System.out.println("Up Command - " + tv);
}
}
public class DownCommand implements ICommand {
private TV tv;
public DownCommand(TV tv) {
this.tv = tv;
}
#Override
public void execute() {
System.out.println("Down Command - " + tv);
}
}
public class ForwardCommand implements ICommand {
private TV tv;
public ForwardCommand(TV tv) {
this.tv = tv;
}
#Override
public void execute() {
System.out.println("Forward Command - " + tv);
}
}
public class BackCommand implements ICommand {
private TV tv;
public BackCommand(TV tv) {
this.tv = tv;
}
#Override
public void execute() {
System.out.println("Back Command - " + tv);
}
}
Keys
public enum Key {
UP, DOWN, FORWARD, BACK
}
TEST
public class RemoteTest {
public static void main(String[] args) throws Exception {
Place home = new Home();
home.getRemoteControl().press(Key.UP);
home.getRemoteControl().press(Key.DOWN);
home.getRemoteControl().press(Key.BACK);
Hotel hotel = new Hotel();
hotel.getRemoteControl().press(Key.UP);
}
}
If you add any additional keys to the remote you don't need to touch any of the existing command or invoker. You just need to add it in the client. This adheres to Open Close principle. If you have different remote for each place then make it as constructor argument, so you no need to change any other classes.
Consider a method
public void doSomething(String actionID){
switch (actionID){
case "dance":
System.out.print("I'm dancing");
break;
case "sleep":
System.out.print("I'm sleeping");
break;
default:
System.out.print("I've no idea what I'm doing");
}
The implementation of the method depends on the value of the parameter. Is there a more elegant way to do this, or a different design pattern to replicate the behaviour?
If the caller decides what logic is executed by passing different strings, then why not just have them call different methods:
public void doSomething(String actionID) {...}
...
doSomething("dance");
doSomething("sleep");
VS.:
public void dance() {...}
public void sleep() {...}
...
dance();
sleep();
It seems like you're unnecessarily funnelling all the calls into doSomething
But the strings might not always be literals. What if you're taking them from the console?
You could provide static mappings from the strings to the corresponding functions:
class MyClass {
private static final Map<String, Consumer<MyClass>> map = new HashMap<>();
static {
map.put("sleep", MyClass::sleep);
map.put("dance", MyClass::dance);
}
public void doSomething(String actionID) {
map.getOrDefault(actionID, MyClass::doNothing).accept(this);
}
public void dance() {
System.out.print("I'm dancing");
}
public void sleep() {
System.out.print("I'm sleeping");
}
private void doNothing() {
System.out.println("I've no idea what I'm doing");
}
}
This makes scenarios where you have a lot of switch cases a lot cleaner.
Introduce an interface, e.g.
public interface HumanState {
public void tellMeWhatYouAreDoing();
}
encapsulate the logic in different implementations
public class DancingState implements HumanState {
#Override
public void tellMeWhatYouAreDoing() {
System.out.println("I'm dancing");
}
}
public class SleepingState implements HumanState {
#Override
public void tellMeWhatYouAreDoing() {
System.out.println("I'm sleeping");
}
}
public class UnknownState implements HumanState {
#Override
public void tellMeWhatYouAreDoing() {
System.out.println("I've no idea what I'm doing");
}
}
and use a map. E.g.
public class HumanStateExample {
public static void main(String[] args) {
HumanStateExample humanStateExample = new HumanStateExample();
humanStateExample.doSomething("dance");
humanStateExample.doSomething("sleep");
humanStateExample.doSomething("unknown");
}
private final HashMap<String, HumanState> humanStateMap;
public HumanStateExample(){
humanStateMap = new HashMap<String, HumanState>();
humanStateMap.put("dance", new DancingState());
humanStateMap.put("sleep", new SleepingState());
}
public void doSomething(String action) {
HumanState humanState = humanStateMap.get(action);
if(humanState == null){
humanState = new UnknownState();
}
humanState.tellMeWhatYouAreDoing();
}
}
I'm not sure how the pattern is called, but it is very useful if you need to delegate the method call based on more than one parameter:
Create a lot of handlers where each one knows when it is responsible for handling a call. Then just loop through them and invoke the first one matching the parameter.
edit: I renamed the class from FancyParameterActionFactory to FancyParameterActionUtility: it is not a factory, the name was misleading
//Your method, but this time with a complex object, not with a simple string.
public void doSomething(FancyParameterObject fpo){
FancyParameterActionUtility.invokeOn(fpo);
}
//The utility which can handle the complex object and decides what to do.
public class FancyParameterActionUtility{
public Interface FPAHandler{
void invoke(FancyParameterObject fpo);
boolean handles(FancyParameterObject fpo);
}
//Omitted: Different implementations of FPAHandler
public static List<FPAHandler> handlers = new LinkedList<>();
static{
handlers.add(new DanceHandler());
handlers.add(new SleepHandler());
//Omitted: Different implementations of FPAHandler
}
public static void invokeOn(FancyParameterObject fpo){
for(FPAHandler handler:handlers){
if (handler.handles(fpo)){
handler.invoke(fpo);
return;
}
}
//Default-Behavior
}
}
Here is a simple implementation of the command pattern based your sample problem. I define a general AbstractCommand abstract class which contains two methods. The first method, createCommand(), instantiates a command class based on an input string name. This is how you can delegate your string input to create the right type of command. The second method is doAction(), and this is left undefined, to be implemented later on by specific concrete command classes.
public abstract class AbstractCommand {
public static AbstractCommand createCommand(String name) {
try {
String clsName = name + "Command";
Class<?> cls = Class.forName(clsName);
AbstractCommand command = (AbstractCommand) cls.newInstance();
return command;
}
catch (Exception e) {
System.out.println("Something went wrong.");
}
}
public abstract void doAction();
}
public class DanceCommand extends AbstractCommand {
public void doAction() {
System.out.println("I'm dancing");
}
}
public class TestCommandPattern {
public void doSomething(String actionID) {
AbstractCommand cmd = AbstractCommand.createCommand(actionID);
cmd.doAction();
}
public static void main(String[] args) {
TestCommandPattern test = new TestCommandPattern();
test.doSomething("Dance"); // should print "I'm dancing"
}
}
Now that this framework has been setup, you could easily add other commands for the various types of actions in your original problem. For example, you could create a SleepCommand class which would output I'm sleeping, or do whatever action you wish.
Hi ive been reading on some similar topics here but none of them answer my question. Some say you cant even do this which is not a good thing since I cant finnish my course in that case.
Heres som simple code. Think of each block as a separate class.
public interface Interface {
void printMessage(String meddelande);
}
public class Model implements Interface {
String message = "hej!";
public static void main(String[] args) {
Model model1 = new Model();
View view1 = new View();
model1.printMessage(model1.message); //Ska jag anropa funktionen såhär ens?
}
public void printMessage(String str) {
}
}
public class View implements Interface {
printMessage(String str) {
}
}
So, how is it now possible to tel the view to print this string from the model class without the classes knowing about each other? Its not allowed to send a reference of the model-objekt to the view-object. ; (
Define an Interface:
public interface MyInterface {
void printMessage(String str);
}
Define a class that can trigger the notification:
public class ClassNotifier {
MyInterface mInterface;
public ClassNotifier(MyInterface mInterface) {
this.mInterface = mInterface;
}
public void triggerTheMsg(String msg) {
if (mInterface != null) {
mInterface.printMessage(msg);
}
}
}
Define a class that will be informed:
public class InformedClass implements MyInterface {
public static void main(String[] args) throws Exception {
InformedClass c = new InformedClass();
ClassNotifier cn = new ClassNotifier(c);
}
#Override
public void printMessage(String newMsg) {
System.out.println("A new msg is here: " + newMsg);
}
}
How does it works?:
this is named a callback parttern, the class ClassNotifier has a reference to the interface MyInterface, which is impl. by Informed class to, so every time the ClassNotifier calls the method printMessage, the method printMessage in the class Informed will be triggered too.
I advice you to use dependency injection, for example:
public class Model {
String message = "hej!";
Interface printer;
public void Model(Interface printer) {
printer = printer;
}
public static void main(String[] args) {
Model model1 = new Model(new View());
model1.printMessage(model1.message);
}
public void printMessage(String str) {
printer.printMessage(str);
}
}
before refactor:
public interface Service {
public void hello(Person p);
}
public class BlackPersonServiceImpl implements Service {
#Override
public void hello(Person p) {
//...
}
}
public class WhitePersonServiceImpl implements Service {
#Override
public void hello(Person p) {
//...
}
}
public class BeforeRefactor {
public static void main(String[] args) {
String str = args[0];
Person p = JSON.parseObject(str, Person.class);
Service service = getServiceFromSpringContainer();
service.hello(p);
}
private static Service getServiceFromSpringContainer() {
//...
return null;
}
}
after refactor:
public interface Service {
public void hello(String str);
}
public class WhitePersonServiceImpl implements Service {
#Override
public void hello(String str) {
Person person = JSON.parseObject(str, Person.class);
//do something to person...
//...
}
}
public class AfterRefactor {
public static void main(String[] args) {
String str = args[0];
Service service = getServiceFromSpringContainer();
service.hello(str);
}
private static Service getServiceFromSpringContainer() {
//...
return null;
}
}
That's what I want(I think "pull down" is not the "right" word to describe it...).
I tried "introduce parameter object" in eclipse, and it does not work.
There are many implementations of "Service". I dont want to change them one by one.
Is there a good way to solve this problem?
Thanks!
You can do it somewhat for a single class and a single method (although it's akward and a succession of small refactoring steps), but not across several types at the same time.
I am dealing with the text extraction from pdf. To this end I wrote my own text extraction
strategy. I have one dynamic class and within this class i invoke text extraction strategy.
However, when i introduce some parameters to my dynamic class i cannot use them within strategy class. To be clear i am adding my code template below.
My question is briefly, is it possible to invoke parameter unq showing up in "get_intro" class, from renderText? Or other way around, can a variable or parameter created inside the "renderText" class be invoked in the "get_intro"?
public class trial {
public trial(){}
public Boolean get_intro(String pdf, String unq){
try { ....
for (int j = 1; j <= 3; j++) {
out.println(PdfTextExtractor.getTextFromPage(reader, j, semTextExtractionStrategy));
}
...} catch (Exception e) {
e.printStackTrace();
}
semTextExtractionStrategy part:
public class SemTextExtractionStrategy implements TextExtractionStrategy {
#Override
public void beginTextBlock() {
}
#Override
public void renderText(TextRenderInfo renderInfo) {
text = renderInfo.getText();...}
#Override
public void endTextBlock() {
}
#Override
public void renderImage(ImageRenderInfo renderInfo) {
}
#Override
public String getResultantText() {
//return text;
return main;
}
}
One could consider the following problematic solution:
public abstract class DefaultTextExtractionStrategy<D>
implements TextExtractionStrategy {
protected D documentInfo;
public final void setDocumentInfo(D documentInfo) {
this.documentInfo = documentInfo;
}
public class SemTextExtractionStrategy extends DefaultTextExtractionStrategy<SemDoc> {
#Override
public void beginTextBlock() {
documentInfo ...
}
public class SemDoc {
public String unq:
}
And in get_intro:
SemDoc semDoc = new SemDoc();
semDoc.unq = unq;
semTextExtractionStrategy.setDocumentInfo(semDoc);
out.println(PdfTextExtractor.getTextFromPage(reader, j, semTextExtractionStrategy));
The problem is that you want to pass some context class on calling the entry function (like ActionEvent or such). But by its name a strategy class probably is a stateless singleton. In the above solution you would need to instantiate from a Class<TextExctractionStrategy>, Class<D> a new strategy instance. Or like in the MouseAdapter class pass the same event class parameter to every method.
This smells of "over-designing" or a skewed pattern application.
As we are on the brink of Java 8 lambdas, you might even consider a "backport" of a design with lambdas.
But for the moment I would go with adding a generic D textExtractionContext to every called function, if the API is not for an external library.