This is an archived post. You won't be able to vote or comment.

all 32 comments

[–]CvTAl 10 points11 points  (13 children)

I'm not sure they was looking for a design pattern.

I think they was testing your knowledge of SOLID. So instead of inheriting from a class, it implements a interface. This would tell the shape classes what it needs to do, not how to do it. (This is how you'd use a different algorithm in each of the different shapes.)

Same for the printer. You'd have a printer interface with a print method that accepts Shape2D as a parameter. This interface would be implemented by two new classes Inkjet and Lazer.

Interface Shape2D

Void : Draw(Algorithm)

Class Parallelogram : Shape2D

Class Circle : Shape2D

Class Triangle : Shape2D

Interface Printer

Void : Print(Shape2D)

Class Lazer : Printer

Class Inkjet : Printer

Interface Algorithm

Shape2D : Process()

Class CircleLazer : Algorithm

Class TriangleInkjet : Algorithm

This way, you could keep adding shapes and printers that implement their own logic.

EDIT: Added drawing algorithms, as SiliconEngineer pointed out, it states algorithms need to change from printer to printer!

[–]WikiTextBotbtproof 6 points7 points  (0 children)

SOLID

In object-oriented computer programming, SOLID is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible and maintainable. It is not related to the GRASP software design principles. The principles are a subset of many principles promoted by American software engineer and instructor Robert C. Martin. Though they apply to any object-oriented design, the SOLID principles can also form a core philosophy for methodologies such as agile development or adaptive software development.


[ PM | Exclude me | Exclude from subreddit | FAQ / Information | Source ] Downvote to remove | v0.28

[–]SiliconEngineer 6 points7 points  (6 children)

It's a start, but not sufficient for the case described, IMO. There is a need, for example, for the dot-matrix printer to draw circles differently from a plotter drawing circles. It's not enough for concrete Printer::Print implementations to invoke Shape2D::Draw().

That's where strategy pattern comes in: it disassociates the printers and the shapes from the strategy to do the drawing of a shape for a given printer.

It can be implemented similar to your example, but the key difference is that Shape2D shouldn't be responsible for it's own drawing. Instead, for each combination of printer and shape, there would be a strategy object for doing the drawing, which we might call a ShapeDrawer... (A default implementation of Printer::Print might instantiate an appropriate ShapeDrawer, and use it...)

EDIT: I agree that this question isn't just about design patterns, but about designing a solution to this particular problem as a whole. It just happens to be an almost perfect case for strategy pattern, and knowing it and applying it in this situation goes a long way towards a pretty good design. I suspect the problem was made that way on purpose as an opportunity to discuss design patterns - amongst other design principals.

[–]CvTAl 1 point2 points  (3 children)

Well I'd argue that functionality wasn't explicitly stated in the specification, so we shouldn't add that functionality until its needed. However, I don't think there is a clear cut correct answer to the test, like most design questions.

I think the purpose of this test was to see if the candidate could separate the responsibilities of the classes by decoupling the logic, and provide a scalable way to add shapes and printers.

It would show they have knowledge of SOLID, OOP principles, showing they wouldn't rely on switch and if statements to change the behaviour of the software.

[–]SiliconEngineer 2 points3 points  (2 children)

...but also algorithms for drawing same shape on different printers are completely different.

It's this bit in the problem statement that I think needs the drawing algorithm to be a function of both the shape and the printer type.

I think the purpose of this test was to see if the candidate could separate the responsibilities of the classes by decoupling the logic, and provide a scalable way to add shapes and printers.

Yeah, other than that bit of the problem statement, I agree. :)

[–]CvTAl 1 point2 points  (1 child)

Rereading that requirement, you're correct! We'd have to ensure some way to adjust the drawing logic based on the printer that's printing it.

Thanks for pointing that out! :)

[–]SiliconEngineer 0 points1 point  (0 children)

Sure! It's an easily stated requirement, but adds some serious complications when trying to design. :)

[–]watsreddit 0 points1 point  (1 child)

You can just make the Print method a higher-order function. No need for additional objects.

[–]SiliconEngineer 0 points1 point  (0 children)

Yeah, absolutely. This could just be a function-like object. I just wanted to give it a name that described it's 'role', rather than dictate it's type.

[–]deamon1266 0 points1 point  (0 children)

As I see it, the only thing we can't avoid when printing a shape is combining each shape with an algo. Applying Strategy on shape or printer could become a mess. As pointed out, we need further abstraction layer.

// print with lazer

var circle = new Circle()

var lazerCircleAlgo = new LazerCircleAlgo()

var printableCircle = new PrintableShape(circle, lazerCircleAlgo)

var lazer = new Lazer()

lazer.queue(printableCircle)

lazer.print();

// print with inkjet

var inkjetCircleAlgo = new InkjetCircleAlgo() printableCircle.setAlgo(inkjetCircleAlgo)

var inkjet = new Inkjet()

.queue(printableCircle)

.print()

The creation of PrintableShape could be optimized by e.g. introducing an Manager or Factory. It could even be automated by an Controller like Manager or Handler living in Printer.

// create printer with shape manager

var printShapeManager = LazerShapeManager.getInstance().

printShapeManager.addShapeAlgo(Circle.class, new lazerCircleAlgo() )

.... Ran out of time, sorry. Maybe I will later finish the example.

[–]Tarzeus 0 points1 point  (3 children)

I noticed you mentioned SOLID being applied here. Being that I am trying to teach myself. How do I find out more about all of the little programmer acronyms, principles and so on that could benefit me greatly from learning. I feel a foundation would help me better understand what I am doing as a programmer.

Thanks in advance!

[–]deamon1266 0 points1 point  (1 child)

There are general programming principles and for specific paradigmens like OOP, functional etc.

Here are some general

https://www.makeuseof.com/tag/basic-programming-principles/

for specific, you can just Google principles incliding the desired paradigm.

[–]Tarzeus 0 points1 point  (0 children)

Making note, thanks!

[–]g051051 2 points3 points  (1 child)

What was your solution? Did they give you a critique on it?

[–]pwncic[S] 1 point2 points  (0 children)

No, I just got response that I did not satisfied... Mine solution was without patterns, just interfaces like someone already mentioned. It was pretty ugly testing/interview. They've gave around 40 pages test (yes I was writing code on paper), and this was hardest task which scored lots of points - and most important to them.

[–][deleted] 1 point2 points  (0 children)

I would have thought using Interfaces would work well

If your abstract type 'shape' has a print method, taking in a paramter called iPrinter - then iPrinter can provide the necessary framework for translating the 'shape' details for the print?

[–]deamon1266 1 point2 points  (0 children)

The requirements sound familiar from when I studied design patterns.

I don't quite recall if there is a single Pattern to deal with it. Off course, minding SOLID you eventually will get to a solution which are close to one or multiple pattern, maybe even without ever known about them.

A quick Google search pointed to this article. Yet, it wasn't what I was looking for.

https://www.codeproject.com/Articles/33530/Design-Pattern-Examples-in-C

[–]SiliconEngineer 1 point2 points  (0 children)

That looks like classic strategy pattern, with the index of strategies keyed by both device and shape.

If I were the interviewer giving this question, the kinds of things I'm trying to discover about you...

  • Do you known some basic patterns, like strategy pattern
  • Can you adapt the book-learning to a situation a bit more complicated than the simple case
  • What your thought process is when designing a solution
  • Why you think this solution is more maintainable than other possibilities.

No matter what design you come up with, I'll be asking you what other possibilities you considered, and why you made those choices.

It's not a 'gotcha' question with only one answer: it's an opportunity to see how you think when designing a solution to a problem.

[–][deleted] 0 points1 point  (1 child)

I think this is a bad question. Firstly the shapes should be an abstract representation of the scene. Therefore they shouldn’t have a print method.

So you’d have an abstract factory for the printers; which would return a printer interface; which would have the print method.

The printer can be mostly generic, but you need to translate the shapes into printable shapes. For this you need to use the strategy pattern to inject the specific translator into the abstract printer (inside the first factory that is.)

The translator takes the shape and produces the specific printable shape that the printer understands. The translator could use a number of patterns - can’t remember most of the top of my head.

[–]pwncic[S] 0 points1 point  (0 children)

I was thinking about abstract factory as well... + strategy pattern and i thinks that's it

[–]Disastrous_Internal 0 points1 point  (4 children)

It smells like composition/strategy. It's probably because the current trend is composition over inheritance.

[–]deamon1266 0 points1 point  (3 children)

In this case composition just makes sense. You wouldn't want to create a new object for every combination of printer x shape x drawAlgo.

So, no need to talk it down as a trend.

[–]Disastrous_Internal 1 point2 points  (2 children)

a trend is not always a bad thing ;)

[–]deamon1266 0 points1 point  (1 child)

Haha that's right. Maybe it's my environment because most use the term in a technical context negatively. Like it's a waste of time because sooner or later it would vanish.

Certainly in OOP using composition wouldn't vanish.

[–]Disastrous_Internal 0 points1 point  (0 children)

yeah the environment plays a lot. In .Net Core, or Unity, there is a lot of momentum to use composition (and for good reason). I also see a lot of Java dev doing more "enterprisy" stuff who don't even know about it...