Lesser Hours Structure

 It's time to return to the Breviary Project.

We are basically aiming at (1) reading in data from several structured sources into a set of offices, from Lauds to Compline and then (2) Displaying it to screen.  So far we've mainly been looking at components of offices as well as sources for some supporting domains (psalms and hymns).  It's time to start looking at the structure of an office itself.

These classes involve, frequently, juggling where to put special cases.  Using a Breviary is full of special cases; the slander that "many times there

was more business to find out what should be read, than to read it when it was found out" (from the original preface to the BCP, written by men who ideologically misunderstood both the origins of and intentions behind the office, but that's another story) isn't actually true, but there's a reason that breviaries come with multiple ribbons to keep place.  Many of the lower-level classes we've already seen hide a lot of the special case logic from the classes making use of them.  As we look at constructors and formatting rules at the office level the same thing will show up.

We will begin with the three minor hours, Terce, Sext, and None, which share a common structure (but different texts within that structure).  because they share so much, all of their structure, and much of the construction complexity, can be put in a base class, LesserHourBase:

class LesserHourBase

{

protected:

  LesserHourBase(const IHoursInfo &inInfo, const OfficeNames inName);

  LesserHourBase(const IHoursInfo &inInfo, const OfficeNames inName,

                 const std::string &inCollect);

  LesserHourBase(const Days inDays, const std::string &inCollect,

                 const OfficeNames inName)

      : m_season(HymnSeasons::NORMAL),

        m_petitions{ std::make_unique<FerialPetitions>(

            ((inDays == Days::SUNDAY) ? Grades::DOMINICA : Grades::FERIA),

            inDays, m_season, inName) },

        m_collect(inCollect)

  { }

  LesserHourBase(const Days inDays, const Grades inGrade,

                 const HymnSeasons inSeason, const std::string &inCollect,

                 const OfficeNames inName)

      : m_season(inSeason), m_petitions{ std::make_unique<FerialPetitions>(

                                ((inDays == Days::SUNDAY) ? Grades::DOMINICA

                                                          : inGrade),

                                inDays, m_season, inName) },

        m_collect(inCollect)

  { }

  explicit LesserHourBase(const SpecialDays inDay);

  virtual ~LesserHourBase();

  virtual std::string getHymnName() const = 0;

  virtual std::string getOfficeName() const = 0;

  void formatOfficeImpl(const IEncapsulatedOfficeFormatter &inFormatter,

                        const IPsalter &inPsalter, const bool inRomanUse,

                        const bool inIsPriest, const IHymnSource &inHymns,

                        const IOfficePsalmSpecSet &inPsalms,

                        const Chapter &inChapter,

                        const IAntiphons &inAntiphons) const;

protected:

  HymnSeasons m_season;

private:

  std::unique_ptr<FerialPetitions> m_petitions;

  std::string m_collect;

  SpecialDays m_triduum = SpecialDays::NONE;

};

This is an abstract class (template method pattern) with a protected constructor.  Let's look at the constructors first.

The simplest are the two that are inlined.  The first is meant for an ordinary day in a "green" season of the year (after Epiphany or after Pentecost/Trinity), the baseline of the offices.  The second is used in the general constructor of the specific offices which is meant for unit tests of the specific classes, but isn't used anywhere else.

The two next constructors make use of helpers which are used in constructing all the offices -- these are the interfaces which mediate the data read in from an XML file.

LesserHourBase::LesserHourBase(const IHoursInfo &inInfo,

                               const OfficeNames inName)

    : m_season(inInfo.getSeason()),

      m_petitions(inInfo.generateFerialPetitions(inName)),

      m_collect(inInfo.getCollect())

}


LesserHourBase::LesserHourBase(const IHoursInfo &inInfo,

                               const OfficeNames inName,

                               const std::string &inCollect)

    : m_season(inInfo.getSeason()),

      m_petitions(inInfo.generateFerialPetitions(inName)), m_collect(inCollect)

}

These are primary constructors for anything but very basic days.

Finally, there's a constructor for the really anomalous Triduum and Easter Week offices, which is all special-case handling:

LesserHourBase::LesserHourBase(const SpecialDays inDay)

    : m_season((inDay == SpecialDays::EASTER_WEEK) ? HymnSeasons::EASTER

                                                   : HymnSeasons::PASSIONTIDE),

      m_petitions(std::make_unique<FerialPetitions>(inDay)),

      m_collect(

          (inDay == SpecialDays::EASTER_WEEK)

              ? "Almighty God, Who through Thy Only-begotten Son Jesus Christ hast overcome death, and opened unto us the gate of everlasting life ; we humbly beseech Thee, that, as by Thy special grace preventing us Thou dost put into our minds good desires, so by Thy continual help we may bring the same to good effect; through Jesus Christ our Lord."s

              : "Almighty God, we beseech thee graciously to behold this thy family, for which our Lord Jesus Christ was contented to be betrayed, and given up into the hands of wicked men, and to suffer death upon the Cross."s),

      m_triduum(inDay)

{

  if (inDay == SpecialDays::NONE)

    throw std::runtime_error(

        "Cannot initialize lesser hour with null special day");

}

The formatting function makes use of the two abstract calls in the interface:

void LesserHourBase::formatOfficeImpl(

    const IEncapsulatedOfficeFormatter &inFormatter, const IPsalter &inPsalter,

    const bool inRomanUse, const bool inIsPriest, const IHymnSource &inHymns,

    const IOfficePsalmSpecSet &inPsalms, const Chapter &inChapter,

    const IAntiphons &inAntiphons) const

{

  inFormatter.formatHeading(getOfficeName());

  if ((m_triduum != SpecialDays::MAUNDY_THURSDAY)

      && (m_triduum != SpecialDays::GOOD_FRIDAY)

      && (m_triduum != SpecialDays::HOLY_SATURDAY))

    {

      inFormatter.formatInvocation(

          inRomanUse, (m_season == HymnSeasons::SEPTUAGESIMA)

                          || (m_season == HymnSeasons::LENT)

                          || (m_season == HymnSeasons::MID_LENT)

                          || (m_season == HymnSeasons::PASSIONTIDE));

      inFormatter.formatHymn(inHymns.getHymn(getHymnName(), m_season),

                             getHymnName());

    }

  inPsalms.outputPsalms(inFormatter, inAntiphons, inPsalter);

  if (m_triduum == SpecialDays::NONE)

    inChapter.formatChapter(inFormatter, inHymns, m_season);

  else if (m_triduum == SpecialDays::EASTER_WEEK)

    {

      inFormatter.formatCanticleVerse(

          "This is the day which the Lord hath made: we will rejoice and be glad in it.");

    }

    m_petitions->formatPetitions(inFormatter, inPsalter, inRomanUse,

                                 inIsPriest);

  inFormatter.formatCollect(m_collect);

  inFormatter.formatDismissal(inIsPriest);

}


Note that the tests on m_triduum are all different, making it somewhat counterproductive to turn this from a flag into a strategy.

This is, as you will note, a protected function; many of the parameters are provided by members of the specific office, one level up.

Let's take Sext as a representative example.  (Terce has a couple of irregularities around the hymn used, which varies during the octave of Pentecost.)

class Sext : private LesserHourBase

{

public:

  Sext(const IPsalter &inPsalter, const IHoursInfo &inInfo,

       const ISextInfo &inSextInfo)

      : LesserHourBase(inInfo, OfficeNames::SEXT),

        m_antiphons(inSextInfo.generateSextAntiphons()), m_psalms(inPsalter),

        m_chapter(inSextInfo.generateSextChapter())

  { }


  Sext(const IPsalter &inPsalter, const Days inDays,

       const std::string &inCollect)

      : LesserHourBase(inDays, inCollect, OfficeNames::SEXT),

        m_antiphons(inDays), m_psalms(inPsalter), m_chapter(inDays)

  { }

  Sext(const IPsalter &inPsalter, const Days inDays, const Grades inGrade,

       const HymnSeasons inSeason, const std::string &inCollect,

       const std::string &inText, const std::string &inSrc,

       std::span<std::string> inResponses,

       const ILaudsAntiphons &inAntiphons)

      : LesserHourBase(inDays, inGrade, inSeason, inCollect,

                       OfficeNames::SEXT),

        m_antiphons(inAntiphons.getAntiphon(OfficeNames::SEXT)),

        m_psalms(inPsalter), m_chapter(inText, inSrc, inResponses,

                                       (inSeason == HymnSeasons::PASSIONTIDE))

  { }

  Sext(const IPsalter &inPsalter, const SpecialDays inDay)

      : LesserHourBase(inDay), m_antiphons(inDay),

        m_psalms(inPsalter, (inDay != SpecialDays::EASTER_WEEK)),

        m_chapter(inDay)

  { }

  void formatOffice(const IEncapsulatedOfficeFormatter &inFormatter,

                    const IPsalter &inPsalter, const bool inRomanUse,

                    const bool inIsPriest, const IHymnSource &inHymns) const;

private:

  std::string getHymnName() const override

  {

    return "Rector potens, verax Deus";

  }


  std::string getOfficeName() const override

  {

    return "SEXT";

  }

  SextAntiphons m_antiphons;

  SextPsalmSpec m_psalms;

  SextChapter m_chapter;

};


The specializations of the two virtual functions are straightforward one-line implementations.  The constructors are simple enough to inline, because complexity is captured either in the base class or in the three members which are themselves specialized for Sext.

The formatting just passes the assembled data plus passed-in parameters to the implementation in the base class:

void Sext::formatOffice(const IEncapsulatedOfficeFormatter &inFormatter,

                   const IPsalter &inPsalter, const bool inRomanUse,

                   const bool inIsPriest, const IHymnSource &inHymns) const

{

  formatOfficeImpl(inFormatter, inPsalter, inRomanUse, inIsPriest, inHymns,

                   m_psalms, m_chapter, m_antiphons);

}


Comments

Popular posts from this blog

Boundaries

State Machines

Considerations on an Optimization