Problem

The only seating strategy is 1455 lines and 35 methods. That is not a complaint about its length: three questions genuinely live there, the ladder that places a party has six rungs, and every one of them earned its place from a reported fault. The problem is that two of the things holding it up are not about seating at all, and a second strategy cannot have them.

The row geometry. rows(), run(), runsOf(), fragments(), isolation() and apart() answer questions about a hall: group these places into rows, find a stretch this long, find the unbroken runs a set of places sits in, say how far one place is from another. None of it knows what a party is or which rung is asking. About 120 lines.

The rate matching. shareOut() and its four helpers decide which seat goes to which rate when the rates price different grades, by augmenting paths. That is a bipartite matcher, roughly 170 lines, and it is a plugin's private method. It is also the part with a real algorithm in it, the one most worth testing on its own and least worth reading past to reach the seating rules.

Two consequences, and the second is the one that matters:

  • Anybody writing a second strategy starts from nothing. There is no base class and no trait, so "seat a party in one row" and "spread a party across a section" would each need their own copy of the geometry and their own matcher, and a copied matcher is a matcher that drifts.
  • The three questions the interface asks are 160 of the 1455 lines, so what the class is reads as a tenth of what it contains.

Filed off the back of #3615212, which added the third question and made the shape hard to keep ignoring: consolidate() needed a new geometry method (runsOf(), because run() answers with the first run only) and reused the matcher through a private call, both of which a strategy outside this file could not have done.

Proposed resolution

Extract the two collaborators, leave the seating rules where they are, and change no behavior at all.

  • A row geometry service or value-object helper in yoyaku_placement, holding the six methods above. It takes places and returns places; it never sees a request, a rate or an anchor.
  • A rate matcher, holding shareOut() and its helpers, plus uniformlyPriced(), prices(), gradeIds() and rateKey(). It takes places and wanted counts and answers with an assignment or nothing.
  • Together keeps propose(), giveUpFirst(), consolidate(), the six rungs and the reduction's two passes, and reads the two collaborators. It should come out near 700 lines, which is what the seating rules honestly cost.
  • Whether a base class or trait follows is a separate decision and should not be made in the same issue. Two collaborators a strategy can inject may well be enough, and a base class invented for one implementation is a guess.

This is a refactor with no user-visible change, and the safety net is already there: the reported faults each pinned a rule, so a rule that moves and breaks is caught by a test naming the case it came from.

Remaining tasks

  • Extract the geometry, unchanged, and point Together at it.
  • Extract the matcher, unchanged, and point Together at it.
  • Cover both directly, at the level they now sit: a stretch shorter than the run asked for, a row with a gap in it, overlapping windows of one stretch, a party in a pool with no row at all; and for the matcher, rates pricing the same grades, rates pricing disjoint grades, an arrangement only the matching finds, and one that exists for no share-out. Some of these are asserted today only through a whole hold, which is a slow and indirect way to test arithmetic.
  • Keep every existing test passing untouched. A refactor that needs its tests edited is not a refactor, so any test that has to change is a behavior change and belongs in its own issue.
  • docs/seating.md, whose "Writing your own strategy" section describes what a strategy is handed, and the cost section, which explains why the geometry reads nothing.

User interface changes

None.

Issue fork yoyaku-3615214

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

  • mably committed c6103a3c on 1.x
    task: #3615214 Split the seating strategy: the row geometry and the rate...
mably’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.