4 ms·
I never got the factory thing. I just put the object creation logic in the object itself as static methods. For example here: https://www.tutorialspoint.com/de
by winrid 3y ago
I never got the factory thing. I just put the object creation logic in the object itself as static methods.
For example here: https://www.tutorialspoint.com/design_pattern/factory_pattern.htm# https://www.tutorialspoint.com/design_pattern/factory_patter...
I'd just have a static Shape.getOfType()
Or maybe ShapeType.toShape() for type safety instead of using strings for the type.
It's okay, I'll brace for the down votes. :)
- viraptor 3y agoFor things where you don't have to know which type of shape you're actually trying to create - sure. But now consider you're making something that displays shapes in one of two GUI toolkits, depending on where you're running it. You have basically 3 options: - if/else everywhere to make ShapeA/ShapeB depending on the current toolkit - generic Shape DTO and if/else + transforming all around the dispatcher to each toolkit - pass in ToolkitContext instance which has a .createShape() which requires a single condition at app initialisation stage Well... that ToolkitContext is a ShapeFactory. (Among other things) Then if you want to use a nicer system of handling this, you use DI, which is then effectively a FactoryFactory. Whether it looks obnoxious each time or you simply say AppContext.Get<Shape>() depends largely on your language though.
- contravariant 3y agoSolving the issue of having two different ways of drawing shapes by having two different classes of shapes and special logic to construct the right one seems like a bad idea. Seems to me that what you actually want is an algebraic data type and 2 different plot functions. Visitor patterns are a neater way to achieve that.
- viraptor 3y ago> seems like a bad idea. "Seems like a bad idea" seems like a bad way to make software engineering decisions. Define what you're losing / gaining, how it impacts readability, does it simplify future development, etc.
- contravariant 3y agoWouldn't one normally require that kind of analysis before embracing some design pattern, rather than dismissing it? I'm just saying what my prior is, I'm not saying I won't change my mind, but you seem to suggesting I should require less evidence to accept the option I'm least sure about. Which, you know, seems like a bad idea.
- deleted 3y ago[deleted]
- winrid 3y agoLet's say you have SwingButton and FLTKButton. I'd have a common Button that uses the fancy new pattern matching features. I like me some if statements. Debugger friendly.
- blowski 3y agoSome engineers see a chain of if statements as the problem, others see it as the solution. Who’s right is very subjective.
- djbusby 3y agoUse switch!
- viraptor 3y agoI'd go with: who's right depends on the code in question, with a large area of "it's a wash" in between. The extremes are not subjective.
- tacitusarc 3y agoI strongly disagree with this. If statements have caused me more headaches than any other single programming concept. This is not subjective. When coders use if statements to replace polymorphism, it scatters logic throughout many different files. This has two extremely negative effects: first, to understand the reach of a single component, you need to understand the entire codebase. Second, when multiple developers are working in tandem, you are almost guaranteed to go through an incredibly difficult rebase process since the structure requires everyone to work on the same files. If statements are useful for validation and bootstrapping decisions*, but if you embed them in your model as part of the operational structure, you’re making a rookie mistake. *High performance software and scripts are the exception to most rules.
- viraptor 3y agoThen you have an if statement per every place where you're operating on one or the other type. It may be very few places and it may be fine like that. It may be 4 options times a hundred paces and then it's not fine anymore. I'm not sure what you mean by debugger friendly. Your debugger knows what types things are.
- paulmd 3y agoI don't quite know what point this feature came to be, but in Java it's now compiler-correct for subclasses to declare their overrides as returning a more precise type than the superclass, as long as it doesn't disagree with the superclass. The place I've bumped into this is the "self"/"builder" pattern of setter. So for example if you have the superclass method: abstract public Shape setArea(Number area); it's absolutely correct for other interfaces to override it with more precise definitions that they expect their members to obey: @Override abstract public Quadrilateral setArea(Number area); @Override public Square setArea(Number area) { this.area = area; return this; } The downside here is that interfaces and classes have to override all such methods with their own implementation returning the proper type, even if it's just { super.setArea(area); return this } otherwise the superclass's looser definition will be used. For example if you don't override the area method then this doesn't work: //compiler error: method setSides is unknown for class Shape new Square().setArea(area).setSides(sides) But if subclasses override all their methods, for example if Square overrides the setArea method with one returning Square, the above example works. Obviously, again, only works if you know the instance you're being passed is a Quadrilateral or a Square, but if you don't know that you are reduced to casting based on some kind of list or handler.
- andor 3y agoShape.getOfType(type) would also be a factory. A factory does not need to be a class, it can also just be a function. I see two reasons for using a factory: 1. Reducing the scope of what a piece of code is responsible for (a.k.a. "Single-Responsibility-Principle"). Assume you are working on a graphics app that can draw 50 different shapes. The currently selected tool is stored in a variable, and there is a long switch statement that returns a new Shape depending on that variable. You wouldn't want the switch statement to dominate the rest of the code: handleClick(x, y) { selectedTool = getSelectedTool(); shape = switch (selectedTool) { case "CIRCLE" -> new Circle(); // 49 more lines here... } drawShape(shape, x, y); } Not only would it get more difficult to read the code, but also the test for handleClick() would have to test for all 50 shapes. If the instantiation of the shape is separated out into its own function, handleClick() can be shorter. If the factory function is injectable, the tests for handleClick() can focus on the coordination work that the function does: it asks the factory for the shape and draws it in the right place. 2. Allowing to reconfigure what kind of objects are created through Dependency Injection. For instance: class CommentRepository { constructor(dataSource, queryFactory) { ... } getComments(articleId) { commentsQuery = queryFactory.getCommentsQuery(articleId); return dataSource.query(commentsQuery).map(toResponseType); } } class PostgresQueryFactory { ... } class RedisQueryFactory { ... }
- winrid 3y agoAlso the fact that you use the repository pattern, which I would absolutely never do even though I know it's fairly common, shows that we have different ideals (no offense). :)
- winrid 3y agoSomething has to map your click events to each of the button classes, and you have to test that code anyway. So I would just put that delegation in the Button class, I don't see the point in the extra abstraction to the factory yet. But to each their own I guess :)
- klysm 3y agoI’ve ended up almost never using static things. You almost always end up wanting one more layer up in my experience, especially for testing. When you make something static, you are fully committing to having that be your highest level. It’s convenient and sometimes makes sense, but the drawbacks are significant