Abstract responsibility from caller without introducing complexity
How can i abstract away that offsetting without introducing additional computational complexity?
Abstraction is a form of complexity. We can argue about the degree of complexity, but I expect everyone to agree that an abstraction adds a non-zero amount of complexity to a codebase.
However, I do agree that this needs to be abstracted away from a caller.
How do i abstract away the offsetting without introducing computaional complexity?
Again, it's impossible to do something that doesn't take any effort. I would rephrase your expectation so that you minimize computational complexity rather than avoid it.
- Every time worldPos change, go trough the data in each child apply the change. localPoints andlocalEdges would now be the actual positions in the world.
This is not a good approach. You're taking the effort to convert all points, even though you don't even know if you're ever going to need them.
Suppose the world position changes, you recalculate everything (e.g. 100 vectors). Only two values are requested before the world position changes again and you have to recalculate everything again. You effectively performed 98 unnecessary conversions.
- Store worldPos in each child aswell and use a custom getter that creates a copy of the desired list, apply the offset to each element in the list and return it
This is closer to what you want, but not good. Now you're storing worldPos in both the parent and the child. What if you forget to update one of them?
At best, you've got duplicate data and are responsible for synchronizing them. At worst, you mismanage the synchronization and will pull your hair out trying to debug it.
A simple fix would be to have the child store a reference to its parent, where it is able to retrieve the (non-duplicate) value.
Another solution would be to always request the values via the parent, not via the child. The distinction here is contextual. Sometimes it's better to access a child via its parents. Sometimes it's better to use the child directly and have the child talk to its parent when necessary.
I assume that you're currently in a scenario where your callers have a reference to the child, not the parent.
A second consideration is how you intend to access these lists. Are you always going to iterate over them? Are you looking for specific items?
Based on your example, I'm going to assume that you're going to iterate over them and therefore won't mind converting the entire list when the list is accessed.
You'll want something like this:
public class Child {
private Parent parent;
public List<Vector2> LocalPoints;
public List<Vector2> LocalEdges;
public Child(parent)
{
this.parent = parent;
}
public List<Vector2> WorldPoints => LocalPoints.Select(localVector => parent.worldPos + localVector).ToList();
public List<Vector2> WorldEdges => LocalEdges.Select(localVector => parent.worldPos + localVector).ToList();
}
This does what you need it to do. Nothing that you're not actually storing the "world" lists. You are simply calculating them on the fly, which means that you don't need to track changes to parent.worldPos
. Whenever you calculate it on the fly, you'll automatically be working with the current value.
If you need further performance optimization when accessing single items from the list (wanting to avoid converting the unwanted items), then you need a more complex solution.
A last consideration: caching. This is a double edged sword.
- If the vectors are fetched many times more than the
worldPos
actually changes, you can squeeze out some performance gains by calculating the values only once when worldPos
changes.
- However, this comes at a great cost: having to manage the caching. This takes a considerable development effort in exchange for a decreased performance effort.
- If the
worldPos
changes more often than you fetch the vectors, then the "cache all" approach will actually decrease performance instead of increase it.
- You would then need a system that caches values only when they are requested the first time, which another level of complexity on top of your "cache all" approach.
This needs to be outweighed. Since offsetting vectors is not a cumbersome calculation (it's literally doing x1+x2
and y1+y2
, I would not look at caching now.
The development complexity of implementing caching does not outweigh the calculation complexity of doing a few extra number additions.
Premature optimization is usually not a good thing. Take note of the potential performance issue but wait until an actual performance problem presents itself and then look for the bottleneck. The bottleneck might be in a completely different location.
new Parent(worldPos, children);
?