MatthewRueben / sock-pairing

Try several sock pairing algorithms as an exercise in writing beautiful Java.
MIT License
0 stars 0 forks source link

Better way to specify MatchableByNumber as the implementation of Matchable to use? #21

Closed MatthewRueben closed 3 weeks ago

MatthewRueben commented 1 month ago

Original Issue scope: Rename MatchableImplementation to ImplementsMatchable? But... ...on the other hand, doesn't it get defined as extends Matchable at one point?

MatthewRueben commented 1 month ago

Renamed this Issue, as the scope has grown.

MatthewRueben commented 1 month ago

Some different approaches to doing this:

  1. Pass the implementing class into SockPairerTester::testAlgorithm() as an argument.
  2. Build the list of socks elsewhere, then pass it in as an argument (that satisfies <T extends Matchable<T> & Comparable<T>>).
  3. Factory design pattern: https://refactoring.guru/design-patterns/factory-method
  4. Builder design pattern: https://refactoring.guru/design-patterns/builder.
MatthewRueben commented 1 month ago

OK, here's the plan:

  1. Put the initial sock creation into its own method (this is where MatchableByNumber will get used).
  2. [New Issue!] Put the sock sequence copier into its own method.
  3. [New Issue!] Memory management for the ordered permutations: use an iterator and release the memory after you're done?
MatthewRueben commented 1 month ago

Ok, so step 1 above just sort of kicks the can down the road, so I'm making it into a new, separate Issue.

MatthewRueben commented 1 month ago

How about an abstract MatchableFactory extended by a concrete MatchableByNumberFactory. You would have it createFirstInstance(), createMatchingInstance(Matchable existingInstance), and startRemakingPastMatches() to reset it. The MatchableByNumberFactory would have a field to track which ID number it's on.

MatthewRueben commented 1 month ago

Ok, but how do we tell Java that the MatchableFactory we're passing in happens to create objects of type T extends Matchable<T> & Comparable<T>? How about instead adding those three methods above as static factory methods required by the Matchable interface? So (once we wrote the implementations in MatchableByNumber), we would do something like:

MatchableByNumber mbn1 = MatchableByNumber.createUnmatchedInstance();
MatchableByNumber mbn2 = MatchableByNumber.createInstanceMatching(mbn1);
// ...
MatchableByNumber.startRemakingPastMatches();

...and then pass MatchableByNumber in via a Class<T> to whatever method is building the sock list? With type bounds, like:

private <T extends Matchable<T> & Comparable<T>> List<T> makeListOfComparableMatchables(Class<T> implementation, int howMany)
{
   T comparableMatchable1 = T.createUnmatchedInstance();
   // ...And so on.
}

...and hopefully passing in MatchableByNumber.class will resolve T to be MatchableByNumber and we're golden.

MatthewRueben commented 3 weeks ago

Trying a type parameter for the whole SockPairerTester class since it's used by multiple functions. That way, I can specify it when instantiating a new SockPairerTester<Whatever> instead of via type inference by passing in a Class<T> argument.

MatthewRueben commented 3 weeks ago

Ok, the new hotness is: <M extends Matchable<? super M>> -- i.e., it's Matchable with objects of its own type as well as of parent types up to some ancestor. I'm still not sure how to grab that ancestor and use it as a second type parameter unless I were to specify it -- i.e., I can't do <M extends Matchable<T super M>>. 1a657f1a394f8f23a6f1af1cde7c245f29b6afac

MatthewRueben commented 3 weeks ago

I also implemented something similar to the static factory methods mentioned above according to the pool of objects pattern. 414140d08a296e132a137eb46532836a69ddcfc1

And I think fixed some other type parameter / generics stuff so that everything is happy now. eb72ec0b8cf9274554b7022993c28cf3e2a8f0a9