Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to an immutable API #68

Open
theofidry opened this issue Apr 25, 2017 · 8 comments · May be fixed by #121
Open

Move to an immutable API #68

theofidry opened this issue Apr 25, 2017 · 8 comments · May be fixed by #121
Milestone

Comments

@theofidry
Copy link
Collaborator

I'm not really sure what's the interest of having an addFilter and co. when everything could be added in the constructor.

@fsmeier
Copy link

fsmeier commented Jun 8, 2017

you mean something like a options-array of filters?
for my current project i have like 15-20 filters added.

@mjpvandenberg
Copy link

The DeepCopy object is a service (as opposed to an entity or value object). Making a service immutable makes no sense and adds no value, imo. You should be able to add filters to a DeepCopy object that is injected (either via constructor or setter) as a dependency into your own application service.

@theofidry
Copy link
Collaborator Author

@mjpvandenberg there is plenty of value for immutable services. The benefits of immutability don't apply only to value objects or entities.

The only thing that changes in this case, is that you know outright all the filters being used as opposed to only appending some filters without knowing what is there before. It's even a bigger of a difference considering that the order of the filters may matter.

@mjpvandenberg
Copy link

Services should be stateless, which implies that there's nothing to mutate.

I guess DeepCopy is not a pure service after all, but a combination of a) a definition of how to deep-copy something, and b) the thing doing the deep-copying according to the definition. It may be appropriate to move part (a) into a separate object that uses the Builder design pattern, especially given that

the order of the filters may matter

@theofidry
Copy link
Collaborator Author

Services should be stateless but that's not always the case (e.g. entity managers or repositories). The built-in filters are stateless, but that's also an extension point where anyone can do anything so there's zero guarantee that they will be.

That said my main concern is not the stateless services as the built-in one are. It's more the order of the filters which matters. Forcing to set all the filters at once is an appropriate and simple solution, and instead of adding a setFilter(), you can just remove addFilter() and move that to the constructor altogether, it's simpler.

So to answer the question "Why to move to an immutable API?", I would say because it's less likely to lead to errors for consumers and make it simpler on our end as well (both for the usage and code-wise).

@mnapoli
Copy link
Member

mnapoli commented Jul 26, 2017

I have to agree that removing the setter solves all those questions. And all containers let you inject stuff in the constructor, so I really don't see a point with the setter too.

@mjpvandenberg
Copy link

I suppose that it would make things simpler and avoid problems with filter order.

My preference would be to replace the addFilter() method with a setFilters() method, as opposed to constructor-only filter definition. It's conceptually equally sound and it can prevent the minor hassle of having to manage multiple DeepCopy configurations in the container.

May I suggest to also add a getFilters() method to expose the current set of filters, so that consumers may re-create the DeepCopy with some additional filters if needed.

@theofidry
Copy link
Collaborator Author

I don't think you should manage multiple DeepCopy config. What I'm thinking of is more exposing a function like mentioned in #69 and consumers on their end can simply define theirs. The DeepCopy instance is not stateless and it's safer to create a new one for each cloning (the overhead is relatively small). So with the filters it would look more like (provided you are overriding everything):

    /**
     * Deep clone the given value.
     *
     * @param mixed $value
     *
     * @return mixed
     */
    function deep_clone($value)
    {
        $useCloneMethod = true;
        $skipUncloneable = true;
        $filters = [
            [new FooFilter(), new FooMatcher()],
        ];
        $typeFilters = [
            [new FooTypeFilter(), new FooTypeMatcher()],
        ];

        $deepCopy = new DeepCopy($useCloneMethod, $skipUncloneable, $filters, $typeFilters);

        return deepCopy->copy($value);
    }

As opposed to right now:

    /**
     * Deep clone the given value.
     *
     * @param mixed $value
     *
     * @return mixed
     */
    function deep_clone($value)
    {
        $useCloneMethod = true;
        $skipUncloneable = true;
        $filters = [
            [new FooFilter(), new FooMatcher()],
        ];
        $typeFilters = [
            [new FooTypeFilter(), new FooTypeMatcher()],
        ];

        $deepCopy = new DeepCopy($useCloneMethod);
        $deepCopy->skipUncloneable($skipUncloneable);

        foreach ($filters as $filter) {
            $deepCopy->addFilter(...$filter);
        }

        foreach ($typeFilters as $typeFilter) {
            $deepCopy->addTypeFilter(...$typeFilter);
        }

        return deepCopy->copy($value);
    }

@theofidry theofidry added this to the 2.0 milestone Oct 15, 2017
theofidry added a commit to theofidry/DeepCopy that referenced this issue Jun 11, 2018
@theofidry theofidry linked a pull request Jun 11, 2018 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants