Surprisingly, C++11 feels like a new language. -- Bjarne Stroustrupgit
至今,C++
社區仍具備強大的生命力,尤爲自C++11
出現後得到了新生。C++
到底存在什麼樣的魅力,讓人如此癡狂呢?本文試圖闡述C++
的設計思惟,並揭示C++
內在的設計本質;最後經過FizzBuzzWhizz
的設計和實現一展C++11
的風采。程序員
Make Simple Tasks Simple.github
Keep Simple Things Simple閉包
Don't Make Complex Things Unnecessarily Complex函數
Don't Make Things Impossible性能
Constraint: Don't Cacrifice Performance.測試
Don’t Over Abstractatom
Abstractionscala
Performance設計
C++
試圖找到「抽象」和「性能」的平衡點,並將抉擇的自由留給了程序員。
No One Size Fits All
Multi-Paradigm
世界是多樣性的,C++
多範式的設計思惟賦予了程序員極大的自由度和靈活性。
More and More Expert Friendly
C++
愈來愈變得更加友好,這種友好性對於專家感觸將更加深入。
FizzBuzzWhizz
詳細描述請自行查閱相關資料。此處以3, 5, 7
爲例,形式化地描述一下問題。
r1 - times(3) -> Fizz - times(5) -> Buzz - times(7) -> Whizz r2 - times(3) && times(5) && times(7) -> FizzBuzzWhizz - times(3) && times(5) -> FizzBuzz - times(3) && times(7) -> FizzWhizz - times(5) && times(7) -> BuzzWhizz r3 - contains(3) -> Fizz - the priority of contains(3) is highest rd - others -> others
接下來我將使用Scala
嘗試FizzBuzzWhizz
問題的設計和實現。
從上面的形式化描述,能夠很容易地獲得FizzBuzzWhizz
問題的語義模型。
Rule: (Int) -> String Matcher: (Int) -> Boolean Action: (Int) -> String
其中,Rule
存在三種基本的類型:
Rule ::= atom | allof | anyof
三者之間構成了「樹型」結構。
atom: (Matcher, Action) -> String allof(rule1, rule2, ...): rule1 && rule2 && ... anyof(rule1, rule2, ...): rule1 || rule2 || ...
藉助C++11
加強了的「類型系統」能力,可拋棄掉不少重複的「樣板代碼」,使得設計更加簡單、漂亮。此外,C++11
構造DSL
的能力也至關值得稱讚,並且很是直接,簡單。
FIXTURE(FizzBuzzWhizzSpec) { Rule spec = makeSpec(); Rule makeSpec() { auto r1_3 = atom(times(3), to("Fizz")); auto r1_5 = atom(times(5), to("Buzz")); auto r1_7 = atom(times(7), to("Whizz")); auto r1 = anyof( { r1_3, r1_5, r1_7 }); auto r2 = anyof({ allof({ r1_3, r1_5, r1_7 }), allof({ r1_3, r1_5 }), allof({ r1_3, r1_7 }), allof({ r1_5, r1_7 }) }); auto r3 = atom(contains(3), to("Fizz")); auto rd = atom(always(true), nop()); return anyof({ r3, r2, r1, rd }); } TEST("fizz buzz whizz") { rule(3, "Fizz"); rule(5, "Buzz"); rule(7, "Whizz"); rule(3 * 5 * 7, "FizzBuzzWhizz"); rule(3 * 5, "FizzBuzz"); rule(3 * 7, "FizzWhizz"); rule((5 * 7) * 2, "BuzzWhizz"); rule(13, "Fizz"); rule(35 /* 5*7 */, "Fizz"); rule(2, "2"); } void rule(int n, const std::string& expect) { ASSERT_THAT(spec(n), eq(expect)); } };
Matcher
Matcher
是一個「一元函數」,入參爲int
,返回值爲bool
,是一種典型的「謂詞」。設計採用了C++11
函數式的風格,並利用強大的「閉包」能力,讓代碼更加簡潔,並富有表達力。
從OO
的角度看,always
是一種典型的Null Object
。
using Matcher = std::function<bool(int)>; Matcher times(int times) { return [=](auto n) { return n % times == 0; }; } Matcher contains(int num) { return [=](auto n) { return toString(n).find(toString(num)) != string::npos; }; } Matcher always(bool value) { return [=](auto) { return value; }; }
Action
Action
也是一個「一元函數」,入參爲int
,返回值爲std::string
,其本質就是定製常見的map
操做,將定義域映射到值域。
using Action = std::function<std::string(int)>; Action to(const std::string& str) { return [=](auto) { return str; }; } Action nop() { return [](auto n) { return stdext::toString(n); }; }
Rule
Composition Everywhere
Rule
是FizzBuzzWhizz
最核心的抽象,也是設計的靈魂所在。從語義上Rule
分爲2
種基本類型,而且二者之間造成了優美的、隱式的「樹型」結構,體現了「組合式設計」的強大威力。
Atomic
Compositions: anyof, allof
Rule
是一個「一元函數」,入參爲int
,返回值爲std::string
。
using Rule = std::function<bool(int, RuleResult&)>; Rule atom(const Matcher& matcher, const Action& action) { return [=](auto n) { return matcher(n) ? action(n) : ""; }; } Rule anyof(const std::vector<Rule>& rules) { return [=](auto n) { auto r = std::find_if(rules.begin(), rules.end(), [&](const auto& r) { return !r(n).empty(); }); return r != std::end(rules) ? (*r)(n) : ""; }; } Rule allof(const std::vector<Rule>& rules) { return [=](auto n) { return std::accumulate(rules.begin(), rules.end(), std::string(""), [=](const auto& result, const auto& rule) { return result + rule(n); }); }; }
Scala參考實現: https://codingstyle.cn/topics/99
Java參考實現: https://codingstyle.cn/topics/100