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

Traits #145

Closed
wants to merge 4 commits into from
Closed

Traits #145

wants to merge 4 commits into from

Conversation

bsideup
Copy link
Member

@bsideup bsideup commented Apr 29, 2016

Depends on #144 , but could be merged instead since it's included.

This pretty huge change makes it possible to use composition over inheritance in TestContainers. All "withXXXX" code was extracted from GenericContainer to the separate traits.

Unfortunately, because of Java limitations, it breaks user's code a little bit - they have to use new GenericContainer<>(...) now (note diamond generic notation).

Motivation:
In our test system, we use "traits" to reuse some code to configure GenericContainer (currently, we have some base container which extends GenericContainer and adds support for a trait, but we want it in core).
For instance, we have a "recipe" to add the Java Agent, and we use it like:

new GenericContainer<>()
    .with(new XHubAgentTrait(some, opts, here))
    .with(new FakeTimeTrait(Duration.ofDays(-7)))
    .withExposedPorts(8080)

Thanks to traits, we don't have to use inheritance to extend our containers - we just apply multiple traits to it.

@rnorth WDYT?

@bsideup
Copy link
Member Author

bsideup commented Apr 29, 2016

* Default settings
*/
@NonNull
private List<Integer> exposedPorts = new ArrayList<>();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

each trait has it's own state, not listed here

@rnorth
Copy link
Member

rnorth commented Apr 30, 2016

This looks good - I've raised some comments but mainly these are just queries.

As for merging this I think we're just about into breaking-change territory, unfortunately. I'm definitely not against jumping up to v2 in the near future, though. I think we just need to pick and choose which other things need to be closed off in 1.0.x, and which other things join this in v2. #87 is probably a prime candidate as it too will have some breaking API changes.

What do you think?

@bsideup
Copy link
Member Author

bsideup commented May 1, 2016

@rnorth thanks for the feedback!

I agree that 2.0 is a better version for such feature because the API layer was dramatically changed, even if public changes are not that huge.

BTW I'll try to research some ways how to workaround that nasty new GenericContainer<> issue. I suspect it to be a bug in javac actually, will discuss with some gurus from Oracle :)

I'm concerned a little bit that this branch might become outdated, but will try to keep it up-to-date (and will start right now because there are some conflicts :) )

@bsideup
Copy link
Member Author

bsideup commented May 1, 2016

@rnorth merged with master

@asafm
Copy link
Contributor

asafm commented May 2, 2016

Hi Guys,

I've been reading through this pull request and here are my 2 cents:

  • I've been reading and then re-reading and then re-reading the code to try to wrap my head around it. The problem with Generics which extends other Generics and other Generics is that you have to keep so much in your head to try to grasp how this works. When compared to the old approach, I believe it add more complexity and makes it harder for people to contribute easily.
  • On the other hand, it clears up the Container interface by separating it to different interfaces and it opens it up to composition which is far simpler than inheritance.

Maybe we can try a bit simpler approach which won't be a breaking change immediately and can be adopted slowly and gain the composition approach.

  • Don't add SelfReference<...> interface to Container - you mainly needed it for Support interfaces
  • Don't add TraitSupport<...> interface. Just add one method from it called with(Trait). directly to Container interface
  • Don't add any *.Support interfaces - leave the methods exactly where they are. Change their implementation to use the new Trait classes. Mark them @deprecated so people can switch to the new methodology slowly. Break it at will in v2 when the community felt users had enough time to adjust.

I'm not sure but maybe this handles the extra <> you had to add to a new GenericContainer call.

What do you think?

@bsideup
Copy link
Member Author

bsideup commented May 2, 2016

Hey @asafm,

Thanks for the feedback!

If I understood @rnorth correctly, he wants to keep "withSomethingSomething(...)" notation, so we can't just mark them deprecated. For me, generic with(Trait) is easier to maintain, but I understand his point as well.

SelfReference was added in #144 to make it possible to extend GenericContainer without losing fluent style of withXXXX calls, so it's not only for Traits.

Of course, best option would be https://projectlombok.org/features/experimental/ExtensionMethod.html , so we could implement support methods ( withSomething(...)) as separate extensions�, but it's not supported in IntelliJ IDEA (yet), and the world is not ready for it :D

@rnorth
Copy link
Member

rnorth commented May 4, 2016

Thanks @asafm. I'm very keen that we keep the library as usable as possible, so if there are concerns over ease of understanding I'd like to be careful to make sure we've considered all the options to simplify. Your suggestions are useful ideas.

If you both wouldn't mind, I'd like to take a bit of time to mull things over and come back with a few proposals - probably skeleton class models rather than full fledged PRs. I'm afraid I'm going to be pretty much offline until next Tuesday, but hopefully will have a bit of time to think about this.

I hope this is OK, and sorry for the delay in responding

@rnorth
Copy link
Member

rnorth commented May 28, 2016

I've not stopped thinking about this one, but TBH I'm afraid this has been slightly pushed down my list by other things outside of the project plus the quite pressing need to equip TC for the 'native' Docker tooling before the end of that beta. That's going to be my main focus for a little while.

Re this PR, I think I'd like to try and simplify a bit - I am a little worried that there's a degree of sophistication which could make this less accessible / more intimidating to users. The kinds of things I'm contemplating, though not necessarily precise, are:

  • I really do want the keep the withX(...), withY(...) methods on GenericContainer for ease of discovery. I think it's reasonably important to keep the implementation either in, or obviously navigable from, these methods too, to make debugging straightforward.
  • I'm wondering whether moving from a trait approach (mutating the type and adding behaviour) towards a less sophisticated plugin pattern (just adding behaviour). I need to check, but I feel this might simplify the type and generics concerns - without removing too much of the power for users to extend by composition. I can imagine it being desirable to add more hook points for plugins.
  • the self type referencing is still definitely something I'd like to keep, as I think it makes it easier to use subclasses of GenericContainer, albeit at the expense of a little extra overhead for people authoring those subclasses.

I hope this is OK for now - I think I'll probably have to return to this topic after native docker support is a bit less of a worry, but thought I should note down my current thinking.

@bsideup
Copy link
Member Author

bsideup commented May 29, 2016

@rnorth sure, if you'ill figure out how to simplify it, but keep composition - I'm all in :)

@bsideup
Copy link
Member Author

bsideup commented Jan 15, 2017

I'll close this PR and start a new one, with less magical API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants