i want to create a polymorphic Queue association. I have two different queues in mind, one is about creating buildings, one is about creating units.
Though the queue has many same properties, like adding or removing items, there are some differences as well.
For instance, a building has a level, while a unit does not. The QueuedItem polymorphic class normally has some methods, like the remove one. However, a remove method is different, depending on whether the item is a building or a unit and needs different handling.
Moreover, the level column is totally insignificant to an army queued item.
So, my question is, based on those needs, what is the best way to design that ? Should i just go ahead with 3 models like (a queued item always belongs to a city):
Building
Unit
QueuedItem -> queued_id, queued_type, city_id
Or i would need to add more intermediate models ? What do you think ?
From your description, it appears you actually need classes for the queues themselves, not just for the queued items. So queue-related methods and operations go in the queue class, and stuff that's related only to buildings or units goes into those classes.
A Queue class would only include methods for adding, removing, and maybe reordering its elements, all of which are unlikely to require different implementations for different types of object. It's better that these methods have no side effects other than adding or removing from the queue - if you need something else to happen after an object is removed, for example, this can be done by the caller of Queue#remove. If do you need the queues themselves to behave differently depending on whether they contain buildings or units, then you'd need a BuildingQueue and a UnitQueue, both of which could inherit their common methods from a basic Queue class, or an included Queue module.
Other stuff specific to buildings and units (such as whether they have a level, or what their specific stats are) would go inside the Building and Unit classes proper. They don't need to know anything about how a queue behaves.
You could create a module with the logic for adding/removing from the queue and include it in both the classes. If you plan to use it in a lot of classes, you could also move it to a plugin, and add the helpers to the models which are to be implemented as queues.
Related
I'm making a method inside a Ruby on Rails app called "print" that can take any string and converts it into a png. I've been told it's not good to make class methods for base ruby classes like String or Array or Hash, etc. so "some string to print".print is probably not something I should do.
I was thinking about making a subclass of String called Print (class Print < String) and storing it in my lib/assets folder. So it would look like: Print.new("some string to print"). So my question is, am I on the right track by 1) creating a sub-class from String and 2) storing it in lib/assets?
Any guidance would be greatly appreciated!
Answers to your question will necessarily be subjective because there are always be many answers to "where should I put functionality?", according to preference, principle, habit, customs, etc. I'll list a few and describe them, maybe add some of my personal opinions, but you'll ultimately have to choose and accept the consequences.
Note: I'll commonly refer to the common degenerate case of "losing namespacing scope" or "as bad as having global methods".
Monkeypatch/Extend String
Convenient and very "OO-message-passing" style at the cost of globally affecting all String in your application. That cost can be large because doing so breaks an implicit boundary between Ruby core and your application and it also scatters a component of "your application" in an external place. The functionality will have global scope and at worst will unintentionally interact with other things it shouldn't.
Worthy mention: Ruby has a Refinements feature that allows you to do do "scoped monkeypatching".
Worthy mention 2: Ruby also lets you includes modules into existing classes, like String.class_eval { include MyCustomization } which is slightly better because it's easier to tell a customization has been made and where it was introduced: "foo".method(:custom_method).owner will reveal it. Normal Monkeypatching will make it as if the method was defined on String itself.
Utils Module
Commonly done in all programming languages, a Util module is simply a single namespace where class methods/static methods are dumped. This is always an option to avoid the global pollution, but if Util ends up getting used everywhere anyways and it gets filled to the brim with unrelated methods, then the value of namespacing is lost. Having a method in a Util module tends to signify not enough thought was put into organizing code, since without maintenance, at it's worst, it's not much better than having global methods.
Private Method
Suppose you only need it in one class -- then it's easy to just put it into one private method. What if you need it in many classes? Should you make it a private method in a base class? If the functionality is inherent to the class, something associated with the class's identity, then Yes. Used correctly, the fact that this message exists is made invisible to components outside of that class.
However, this has the same downfall as the Rails Helper module when used incorrectly. If the next added feature requires that functionality, you'll be tempted to add the new feature to the class in order to have access to it. In this way the class's scope grows over time, eventually becoming near-global in your application.
Helper Module
Many Rails devs would suggest to put almost all of these utility methods inside rails Helper modules. Helper modules are kind of in between Utils Module and Private Method options. Helpers are included and have access to private members like Private Methods, and they suggest independence like Utils Modules (but do not guarantee it). Because of these properties, they tend to end up appearing everywhere, losing namespacing, and they end up accessing each other's private members, losing independence. This means it's more powerful, but can easily become much worse than either free-standing class/static methods or private methods.
Create a Class
If all the cases above degenerate into a "global scope", what if we forcibly create a new, smaller scope by way of a new class? The new class's purpose will be only to take data in and transform it on request on the way out. This is the common wisdom of "creating many, small classes", as small classes will have smaller scopes and will be easier to handle.
Unfortunately, taking this strategy too far will result in having too many tiny components, each of which do almost nothing useful by themselves. You avoid the ball of mud, but you end up with a chunky soup where every tiny thing is connected to every other tiny thing. It's just as complicated as having global methods all interconnected with each other, and you're not much better off.
Meta-Option: Refactor
Given the options above all have the same degenerate case, you may think there's no hope and everything will always eventually become horribly global -- Not True! It's important to understand they all degenerate in different ways.
Perhaps functionality 1, 2, 3, 4... 20 as Util methods are a complete mess, but they work cohesively as functionality A.1 ~ A.20 within the single class A. Perhaps class B is a complete mess and works better broken apart into one Util method and two private methods in class C.
Your lofty goal as an engineer will be to organize your application in a configuration that avoids all these degenerate cases for every bit of functionality in the system, making the system as a whole only as complex as necessary.
My advice
I don't have full context of your domain, and you probably won't be able to communicate that easily in a SO question anyways, so I can't be certain what'll work best for you.
However, I'll point out that it's generally easier to combine things than it is to break them apart. I generally advise starting with class/static methods. Put it in Util and move it to a better namespace later (Printer?). Perhaps in the future you'll discover many of these individual methods frequently operate on the same inputs, passing the same data back and forth between them -- this may be a good candidate for a class. This is often easier than starting off with a class or inheriting other class and trying to break functionality apart, later.
Say, I have a Rails model with a method:
class Order < ApplicationRecord
def process
do_some
do_some_more
do_even_more_here
end
end
Its purpose is solely Rails-model based, meaning its actions are performed on the object itself. What would be the best design pattern to adapt if I'd want to refactor this method and move it out somewhere else?
What I've found so far is that a Decorator shouldn't be the answer as they are more view related. ServiceObject is meant to only perform calculations without the ActiveRecord object and Concerns are mixins that divide responsibilities over several (ActiveRecord) objects.
What I've found so far is that a Decorator shouldn't be the answer as
they are more view related.
Yes, the decorator pattern is most often used in Rails apps to decorate models with view specific behavior. That does not necessarily mean that you can't use the pattern for other purposes.
Rather what you should consider is that models even with no code instead are already fat objects due to the insane amount of features that they get from ActiveModel and ActiveRecord:
validations
persistence
dirty tracking
type casting
querying
etc
A decorator may just add more fat even through the code is neatly tucked away in a separate class.
ServiceObject is meant to only perform calculations without the
ActiveRecord object
Stop listening to whoever told you this. The ServiceObject pattern is really just about creating single purpose objects that do one single job and do it well.
ActiveJob is an example of the ServiceObject pattern. And yes it even has an API for passing in ActiveRecord objects. What you are describing sound like a prime candidate for a ServiceObject or ActiveJob.
Concerns are mixins that divide responsibilities over several
(ActiveRecord) objects.
Mixins don't divide responsibilities. They share behaviors between classes. Its basically like copy-pasting the same methods between a set of classes.
Service object is the one you are looking for.
Mixins and concerns are just a bunches of code you want to mix and share, they can be anywhere.
Make a service object, design it’s public and private interfaces. If you are thinking it’s coming too fat just separate them, easy as that. It might worth reading about SOLID.
In my current project, I have Actors that want to move around in an environment. Different Actors may have different movement Strategies, and I am injecting the Strategies as dependencies into the Actors as follows (language-independent):
actor = new Actor(new Strategy());
However, the strategy needs to be able to make decisions based on the Actor's state (like its current position, health, etc). Therefore, the Actor would need to be injected into the Strategy, and this is obviously bad design since it is circular dependency.
After reading this article, I attempted to extract some state information from Actor into a new class called State and now have a dependency model that looks like this:
state = new State();
strategy = new Strategy(state);
actor = new Actor(state, strategy);
This eliminates the circular dependency. However, in my project, Actor is derived from a library class which has state information already (position, health, etc). Therefore, it doesn't really make sense to extract a third State class when Actor already has that responsibility. All State would be doing is grabbing the state from Actor, so at worst they are still circularly dependent, or at best they are strongly coupled.
What is the best way to handle a situation like this? In the end, I'm trying to inject movement strategies into Actors where the strategies need to know some state information to make smart decisions, and Actor extends a library class which contains state.
Strategy isn't really something explicitly owned by any particular Actor. It should probably be a singleton that takes Actor as a method parameter.
In C# this allows you to implement MEF to its fullest.
I'm currently working on a Rails project, and have found times where it's easiest to do
if object.class == Foo
...
else if object.class == Bar
...
else
...
I started doing this in views where I needed to display different objects in different ways, but have found myself using it in other places now, such as in functions that take objects as arguments. I'm not precisely sure why, but I feel like this is not good practice.
If it's not good practice, why so?
If it's totally fine, when are times that one might want to use this specifically?
Thanks!
Not sure why that works for you at all. When you need to test whether object is instance of class Foo you should use
object.is_a? Foo
But it's not a good practice in Ruby anyway. It'd much better to use polymorphism whenever it's possible. For example, if somewhere in the code you can have object of two different classes and you need to display them differently you can define display method in both classes. After that you can call object.display and object will be displayed using method defined in the corresponding class.
Advantage of that approach is that when you need to add support for the third class or a whole bunch of new classes all you'll need to do is define display method in every one of them. But nothing will change in places where you actually using this method.
It's better to express type specific behavior using subtyping.
Let the objects know how they are displays. Create a method Display() and pass all you need from outside as parameter. Let "Foo" know to display foo and "Bar" know how to display bar.
There are many articles on replacing conditionals with polymorphism.
It’s not a good idea for several reasons. One of them is duck typing – once you start explicitly checking for object class in the code, you can no longer simply pass an instance of a different class that conforms to a similar interface as the original object. This makes proxying, mocking and other common design tricks harder. (The point can be also generalized as breaking encapsulation. It can be argued that the object’s class is an implementation detail that you as a consumer should not be interested in. Broken encapsulation ≈ tight coupling ≈ pain.)
Another reason is extensibility. When you have a giant switch over the object type and want to add one more case, you have to alter the switch code. If this code is embedded in a library, for example, the library users can’t simply extend the library’s behaviour without altering the library code. Ideally all behaviour of an object should be a part of the object itself, so that you can add new behaviour just by adding more object types.
If you need to display different objects in a different way, can’t you simply make the drawing code a part of the object?
Which one of Queue's subclasses is a 'plain ordinary' queue?
(1) java.util.Stack is a legacy class from Java 1.0. It predates the Collections framework by many years, and it's frankly an example of horrible design on many fronts. Nothing about it is the way things should be. The main problem is that Stack extends Vector, and as all inheritance in Java is public inheritance, all the methods of Vector are available on Stack as well. Therefore, you can inspect any position in a Stack, add and remove elements from the middle, clear it, or do any number of other things that should not be part of a stack abstraction, without a cast. Contrast that to using the Queue or Deque interfaces, through which only stack-appropriate methods are available.
(2) There really is no such thing as a plain ordinary queue, but LinkedList implements Queue without any special semantics, so maybe that's what you want.
Any of its concrete subclasses can be used as a "plain ordinary" queue. True, they may have additional functionality, but you're free to use only the methods declared in the Queue interface. Though I imagine you might want to avoid the subclasses whose extra functionality affect queue ordering (like PriorityQueue), capacity, etc. if you want an "ordinary" one.