Ditch the OOP
The member functions probably don’t need to be. You can have the Parser
class provide a few primitive operations (accept character, backtrack, &c.) and implement the rest of the parser as free functions taking a Parser
reference. They can live in the source file (under an anonymous namespace) thus keeping the header nice and minimal.
In this sense, Parser
is really a minimal wrapper around a stream state.
Also, it helps to factor out combinators such as many
, choice
, and so on, to avoid writing error-prone and verbose manual loops.
Sketch
In Parse.h
, your entire public API.
unique_ptr<const Program> parse(istream&);
Your parser state can be entirely private to Parse.cpp
.
struct Parser {
Parser(istream& stream) : stream(stream) {}
bool accept(const char expected) {
char actual;
if (!stream.get(actual)) return false;
if (expected == actual) return true;
stream.seekg(-1, ios::cur);
return false;
}
template<class F, class O>
bool accept_if(F predicate, O output) {
char actual;
if (!stream.get(actual)) return false;
if (predicate(actual)) {
*output++ = actual;
return true;
}
return false;
}
void expect(const char expected) { if (!accept(expected)) throw ...; }
void push_mark() { marks.push_back(stream.tellg()); }
void pop_mark() { stream.seekg(marks.back()); drop_mark(); }
void drop_mark() { marks.pop_back(); }
private:
istream& stream;
vector<istream::pos_type> marks;
};
The public API implementation just forwards to the start production of a grammar with a new parser.
unique_ptr<const Program> parse(istream& stream) {
return parse_program(Parser(stream));
}
Output iterators are a fairly convenient way to produce multiple results.
template<class F, class O>
bool many_if(Parser& parser, F predicate, O output) {
bool success;
while (parser.accept_if(predicate, output)) success = true;
return success;
}
Pointers have the advantage of a conversion to bool.
unique_ptr<const Expression> number(Parser& parser) {
string digits;
auto append = back_inserter(digits);
return many_if(isdigit, append) ? make_unique<Number>(digits) : nullptr;
}
And so on. The real strengths of C++ to leverage when writing parsers are:
- Generic programming
- Iterators
- Streams
Objects, not so much.
I think the major problem here is Shvets's anti-pattern description is flawed, that is why you have problems to distinguish it from the Command pattern:
The name is flawed because it uses an older, existing term "Functional Decomposition" together with the attribute "anti-pattern".
This term was AFAIK not associated with the notion of a "pattern" or "anti-pattern" before, but by picking it for his topic Shvets gave it a new, confusing meaning. The original meaning in programming is "taking a complex process and breaking it down into simpler parts", and that is how I would use the term still today. (Note the Wikipedia definition is about the term more generally in mathematics, but I think this is probably not really helpful in our programming context.) See also this former SE post, where other people already had problems with the anti-pattern name, the top answer suggests the name "procedural decomposition" instead, but that name might lead to similar problems.
The first three symptoms you cited from the pattern description are indeed a good fit for command objects, and sometimes also for other, well desgined classes. That is exactly the reason why they are IMHO not a good indicator for this kind of "degenerated architecture" Shvets describes. See, for example, this former SE post about "classes with a single action as function"
What Shvets probably had in mind was a situation where people try to shoehorn procedural code into classes. This happens most often on a completely different scale (he mentioned "Application Scale") than the scale of a typical command object, which will often be just a small class. It results in code which is not SOLID, containing too large classes, missing proper abstractions, and classes where in most cases everything could be static, since the instantiated "object" is not an abstraction for anything else but a "module" or "sub-program", existing once and with the same lifetime as the whole application process.
Command objects, however, are more an object-oriented workaround for situations where the programming language does not provide an inbuilt mechanism for higher order functions or closures. Rember the term "Command Pattern" has its roots in the GOF's "Design Patterns" book from 1995, a time when C++ and Java were dominant, and both of these languages did not provide such mechanisms at that time. So whenever you need to pass a function around in your program as an abstraction of its own, best choice was probably to use the Command Pattern.
Today, both of these languages (as well as other modern languages) provide additional mechanisms for encapsulating functions in a data type, so often you do not even need to use the Command Pattern (in its original OO form) any more. It still can be useful if your "commands" need a more complex interface than just for the "action" or "function" which they encapsulate.
Best Answer
To answer that question, I'm going to ask you a rhetorical question about another structure that have similar property to the DOM elements that jQuery manipulates, that is the good old iterator. The question is:
How many operation do you need on a simple iterator?
The question can be answered easily by looking at any Iterator API in a given language. You need 3 methods:
That's all you need. If you can perform those 3 operations, you can go trough any sequence of elements.
But that is not only what you usually want to do with a sequence of elements, is it? You usually have a much higher level goal to achieve. You may want to do something with every element, you may want to filter them according to some condition, or one of several other methods. See the IEnumerable interface in the LINQ library in .NET for more examples.
Do you see how many there are? And that is just a subset of all the methods they could have put on the IEnumerable interface, because you usually combine them to achieve even higher goals.
But here is the twist. Those methods are not on the IEnumerable interface. They are simple utility methods that actually take a IEnumerable as input and do something with it. So while in the C# language it feels like there are a bajillion methods on the IEnumerable interface, IEnumerable is not a god object.
Now back to jQuery. Lets ask that question again, this time with a DOM element.
How many operation do you need on a DOM element?
Again the answer is pretty straightforward. All the methods you need are methods to read/modify the attributes and the child elements. That's about it. Everything thing else is only a combination of those basic operations.
But how much higher level stuff would you want to do with a DOM elements? Well, same as an Iterator: a bajillion different things. And that's where jQuery comes in. jQuery, in essence provide two things:
If you take out the sugared form, you realise that jQuery could easily have been written as a bunch of functions that select/modify DOM elements. For example:
...could have been written as:
Semantically it's the exact same thing. However the first form has the big advantage that the order left-to-right of the statements follow the order the operations will be executed. The second start in the middle, which makes for very hard to read code if you combine lots of operations together.
So what does it all mean? That jQuery (like LINQ) is not the God object anti-pattern. It's instead a case of a very respected pattern called the Decorator.
But then again, what about the override of
$
to do all those different things? Well, that is just syntactic sugar really. All the calls to$
and its derivatives like$.getJson()
are completely different things that just happen to share similar names so that you can immediately feel that they belong to jQuery.$
performs one and only one task: let you have an easily recognizable starting point to use jQuery. And all those methods that you can call on a jQuery object are not a symptom of a god object. They are simply different utility functions that each perform one and only thing on a DOM element passed as an argument. The .dot notation is only here because it make writing code easier.