patterns

#define patterns: \ I-------------------------------------------------------------------------------------\ I-------------------------------------------------------------------------------------\ I-------------------------------------------------------------------------------------\ I \ I /$$$$$$$ /$$ /$$ \ I | $$__ $$ | $$ | $$ \ I | $$ \ $$ /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$$ \ I | $$$$$$$/|____ $$|_ $$_/|_ $$_/ /$$__ $$ /$$__ $$| $$__ $$ /$$_____/ \ I | $$____/ /$$$$$$$ | $$ | $$ | $$$$$$$$| $$ \__/| $$ \ $$| $$$$$$ \ I | $$ /$$__ $$ | $$ /$$| $$ /$$| $$_____/| $$ | $$ | $$ \____ $$ \ I | $$ | $$$$$$$ | $$$$/| $$$$/| $$$$$$$| $$ | $$ | $$ /$$$$$$$/ \ I |__/ \_______/ \___/ \___/ \_______/|__/ |__/ |__/|_______/ \ I-------------------------------------------------------------------------------------\ I-------------------------------------------------------------------------------------\ I-------------------------------------------------------------------------------------I # closely related subject "Data structures" # good first introduction to OOP patterns Head First Design Patterns by Eric Freeman and Elisabeth Robson GIGO:"Garbage In Garbage Out"/"RIRO"/"Rubish In Rubish Out" • derived from LIFO/FIFO • the concept that incorrect input produces incorrect output • in practice used to describe behaviour that accepts faulty input and produces some output without raising an error Strings: Escaping: { "I don't know what \"irony\" is." // quoting in C } • it is common that some characters hold special meaning inside strings • string literals are usually delimited from code using some characters • escaping is the mechanism to remove special significance from a character inside a string • in any sane system, escaping is either done by prepending a backslash or doubling the character • the escape character being a prefix has a practical significance for parser implementations Format_strings: • the forefather of string interpolation • a string contains placeholders, which will be filled using variables passed to a format function • C's "printf" is the most well-known example Interpolation: # basic interpolation in Python title = "Example" print(f"--- {title} ---") # Out: --- Example --- • when a string literal is allowed to contain expressions, which will be expended at runtime — it is possible to interpolate: • variables • function calls • child process outputs • most interpreted language support it to some degree • true interpolation with symbol support requires atleast some weak mechanism of reflection • in languages without any reflections, its often emulated using maps { https://github.com/agvxov/ministach } Templating: • interpolation with logic • a library providing templating is commonly referred to as a "templating engine" • a good templating engine should not be aware of the global context (only allowing parameterization) and should only support simple logic; a good example to what happens otherwise is PHP Lorem_ipsum:"Ipsum" • many applications require dummy text to test on • "Lorem ipsum..." is a well known nonsensical, Latin sounding text, originally developed to test in the printing industry • most environments will have some method of fetching ipsum text • it is generally a good idea to test on ipsum text /* control:: ___ _ _ / __|___ _ _| |_ _ _ ___| | | (__/ _ \ ' \ _| '_/ _ \ | \___\___/_||_\__|_| \___/_| */ For_loop: • introduced in B, but no one cares about B, so its attributed to C • older laguages only had logical subsets for integers, not arbitrary statements • common even outside of the C language family • a loop with initialization code, a condition and post loop code ○ flow chart for(A; B; C){ D } [...]: A B C D +-------------+ | A | +-------------+ | | | | V A / \ / \ / \ False +------------> B ----------+ | \ / | | \ / | | \ / | | V | | | | | | | | | True | | | | | V | | +-------------+ | | | D | | | +-------------+ | | | | | | | | | | | | | | V | | +-------------+ | +---------- C | | +-------------+ | | | | | | +-------------+ | | [...] |<------+ +-------------+ Zahns_construct: • orignates from a paper of Zahn • mainstream languages do not implement it as of 2024 • abstraction over quiting blocks to avoid goto-s and extra state variables • blocks can be exited with in situations, each situation is added as a case at the end of the block { // Zahn's construct in pseudo code loop until found or missing; do for i in 1..100 do for h in 1..100 do if myTable[i, h] = target then found; missing; endloop; then found: print "Item was found."; missing: print "Item was not found."; end; } Lambdas: • anonymous function • can be passed around, usually by reference • the lambda expression "<...-1> -> <...-2>" is pronounced "<...-1> becomes <...-2>" or "<...-1> for which <...-2>" Error_handling: Return_value_based: • if a function encountered an error, it gives back a special value Null: NULL nullptr None Nill • a concrete value reserved for signalling "no value at all" • naming originates from the common practice of returning a pointer to the address 0x0 where a heap address is expected, but the operation failed • the null value can be easily tested and ideally passed on to functions that except a real value { free(NULL); } Object: • a null object is a real object instance with its inner state set to some default and all methods performing a no-operations • can be passed to any function or otherwise operated on safely without having to check for errors • helps readability • redundant functions calls will be issued left and right • makes sense if error handing would be messy and errors are expected to occur very rarely Side_effects: • on error, some value is set; usually a global or an extra parameter • common in C with errno or <mylib>errno Exceptions: • an exception is an event unwinding the stack until a handler is found • the trigerring of an exception is commonly called "throwing" or "raising" • the handling of an exception is commonly called "catching" • cannot be ignored; while a NULL value returned could go unnoticed until a cryptic crash, an unhandled exeption will crash right at the spot • slow • best used when errors are unlikely, but important { // Typical exception abstraction // the try block marks the scope of a subsequently define handler try { // This is where the code goes which could possibly throw int i = f(); // assume f() should return an int >0 if (i == 0) { // oh no, an error occured, how unexpected throw Exception(); // we raise an exception, stack unwiding starts here; // its quite common that languages only allow specific // types to be thrown, which usually has to derive // from a vendor provided Exception class print("message"); // execution could never reach this point, // similarly how could after a `return` would // be jumped over too } print("this only executes if f() suceeded") } catch (...) { // after we threw, excution will continue from here ; // we do whatever error handling, e.g. issuing a warning to the user } finally { // additional abstaction over the conventional exception pattern ; // this code will execute even if some other segment of the exception // block tries to jump in execution (return included) } ; // whatever has happened inside the exception block, execution will continue // from here (assumining our process is still running) } Monad:"A monad is a monoid in the category of endofunctors." - easy • "programmable semicolons""compile-time variation of the decorator pattern" — a monad is: • a null value • logic wrapping the null value ○ examples • builders are monads • decorators can be monads if they have error checking logic • C's NULL and "if (myvalue != NULL) { return; }" form a monad (truth be told, a very crude version) • (modern) haskell is built on the idea of monads and has pure nomadic types • the ideal monad is lazily evaluated with no redundant function calls /* design:: ___ _ | \ ___ __(_)__ _ _ _ | |) / -_|_-< / _` | ' \ |___/\___/__/_\__, |_||_| |___/ */ Regex: "Grammar/Regular Expressions" Compiling: • regex strings are compiled into an intermediate form (e.g. non-deterministic finate state machines) which is a computationally expensive operation, however it greatly reduces searching/matching time, paying back after repeated usage • compiling is not only a performance decision, it allows the programmer to arbitrary abstract the problem, allowing for much more comprehensible debugging and promoting extendableness Macthing: • match objects are not bloat • even the most minimalistic implementation will require signalling both the position of the match and its width, even if groups and whatnot are to be ignored | Intermediate | Transformation | Practical | State | | Result I`````````````````````I ┌────────┐ I Compile I │"1[01]+"│ -------------> I~~~~~~~~~~~~~~~~~~~~~I └────────┘ I regcomp() I I std::regex::regex() I I re.compile() I I.....................I | +--------------------------------+ | V ┌────────────────┐ I````````````````````I │ Regex Type │ ---------> I Matching I │────────────────│ I~~~~~~~~~~~~~~~~~~~~I │ regex_t │ I regexec() I │ std::regex │ I std::regex_match() I │ re.Pattern │ I <regex>.match() I └────────────────┘ I....................I | N^^^^^^^^^^^^^^^^N +-------------------> N Match Type N N================N N regmatch_t N N std::smatch N N re.Match N N::::::::::::::::N | +-----------------+-- - - - - - --+ | | | V V V .^^^^^^^^^. .^^^^^^^^^. .^^^^^^^^^. | Group 0 | | Group 1 | | Group N | '.........' '.........' '.........' Singleton: Image: /* + .~~~~~~~~~~~~~. X .' , .' :`.''. + .' ; : : -`.' : '. .' .;-' ...-` '. | ._---'' | + X | _) | | .' .-.,.'., | | '..-' . :. '. | '. . .`' : .' '.`` '`--_ : .' '. '__ \.' + '~~~~~~~~~~~~~' X X */ "The Earth in the emptiness of space.""egyke"^HU • an object that can have only 1 instance • usually accomplished by making the constructor private and adding a special function wrapping construction, checking for other instances • unlike a set of globals, it could practice polymorphism • unlike a set of globals, it could be lazily eval-ed, freeing a potentially considerable amount of memory when not in use • commonly overused / abused in radically OO languages • if your singleton keeps returning the same instance, genious job, you manged to recreate a procedural global in OOP • if your reasoning for making a singleton is "ugh my application will only need one", consider choosing a new career • as a comperasion to how idiotic it is to create a "MainApplication" singleton or a "MyMainComponentThatINeedFromBeginingToEndAndCouplingCloselyAnyways", if you read the source code of AlbertLauncher you will see that they set a static flag whenever main is called so they can conditionally crash if main() were to be called recursively; the logic is the same, so might as well go ahead and do that from now to forever on; in fact, lets apply that to every function that we assume should not be called recursively { // simple singleton example in C++ ; thread safety should considered in the real world; do not replicate at home class A{ static obj_count = 0; int i; A() { this->i = 42; } ~A() { --obj_count; } public: A* init(){ if(obj_count == 0){ ++obj_count; return new A; }else{ return nullptr; // or alternatively, the already created instance } } } } Access: // Move; ?! • when code is protected from being executed or data is protected from being read/written from some code segments • usually implemented at compiletime, but runtime checks qualify too ○ common access levels public - accessible from anywhere {C++, C#} private - accessible only from inside the current class {C++, C#} protected - accessible only from inside the current class and its children {C++, C#} internal - accessible from only the current compilation unit / assembly {static in C/C++, C#} Opaque: • an object whichs every field is private • working with opaque types is only possible through member functions Getter_setter_situation: — getter: • a getter is a method which takes only the object as a parameter and returns some value which is representitive of the objects inner state • may or may not directly correspond to an internal variable — setter: • a setter is a method whichs only purpose is to modifies the inner state of an object based on the passed values — language support: • the real problem why everyone complains about them is that they are bloody annoying; from quick prototyping you are thrown into a den where you have to implement 5 getters and setters or else! C#-like: • auto generate trivial setters/getters with quick keywords Java-like: • auto generate trivial getters for special record class fields Kotlin-like: • allow getter setter functions to pretend to be raw fields; meaning you can change the implementation anytime; most of the time, the api client really doesnt care whether its a function or a value, however theoretically it could lead to footgun situations where values end up all fucked up and hunting down the error is a nightmare • best { // One morning you decided to write a simple rectangle class: class Rect { public: int w, h; int area() { return w * h; } }; // After 1 (one) minute of hardword, you decided to publish // rect.hpp as a header only library and go to sleep. // Uppon awakening you are greeted with hundreds of angry emails. // Here is one of them: "Dear (You)," "We, at UltraSonicBearTrap Inc., have been using your open source project\ rect.hpp. Much of our critical infrastructure is dependent on it in fact.\ However, our new product UltraSonicBearTrap-4670™ runs on our in house\ architecture: Arch YIKES. On Arch YIKES calculating a product takes\ approximetly 20 mins. For this reason, we are left no choice, but to\ store the area and only update it when the sides are updated.\ Which is something that our code dependent uppon your library is unable to allow.\ So, with a heavy heart we must let rect.hpp go." "P.S. FUCK YOU WE WILL HAVE TO EDIT OVER 1'000'000'000 LINES" // Reflecting on your life in a moment of silence you think about what you could // have done differently: class Rect { int _w, _h, _area; public: void set_w(int w) { _w = w; area = _w * _h; } void set_h(int h) { _h = h; area = _w * _h; } int area() { return _area; } }; // With a setup like this, Rect could have been redefined by the // beartrap crew without having to change any code else where. --- // From the above tale we can conclude that every class ever // for any reason in any language MUST be opaque. // Oh, whats that? Look! Over there! Its a new framework! // Go on now, you better rewrite your frontends in it! --- // Alright, now that the grifters have left, // lets discuss the anecdote for real. // The textbook take-away is: // >accessing fields over getters/setters allow for greater flexibility // True. But flexibility is only one aspect of software. // It nowhere implies // >private access is king! hail to him! // Examine the situation more closely: // >we were making a library // >the library promised to be universal // >the library was used as a founding stone // It screams "I HAVE TO BE AS FLEXIBLE AS POSSIBLE". // In contrast, at academia or in online education, on day one // you are asked to write a Dog class, with 3 fields and // 6 functions, as a sacrifice to Ganesha. // Opaqueing types is considered "best practice" (otherwise know as "dogma"), // and questioning how writing 3 times the amount of boiler plate as substance // could possibly be a "best practice". // I wont go into details (here), but this pattern seems to apply to the // rest of OOP too. // No wonder "OOP is junk" seems to be a new trendy opinion. // Notice something else about the short story: its UTTERLY insane. // Flexibility is about the number of assumptions taken. // Operator* not taking 20 minutes is a considerable safe assumption. // The change UltraSonicBearTrap Inc. proposes is gigantic, // yet minimal change is expected from some random piece of code. // You should write them an email back right now, saying: // "No, fuck YOU. You should have known to write your own." // Like many patterns getters/setters arise from the need to hide complexity. // If there is no complexity, they are wastes of bytes. // E.g.: unique_ptr<map<int, vector<time_t>>> a; // Yeah, the next dev in the room does not care about unique_ptr, // he wishes to deal with raws for the life time of the object; // we may also wish to swap out std::vector later on; // it may also turn out that we can get away with unordered_map // or might wanna roll our own implementation. // Better hide it! int x; // Are you fucking high? --- // Border line is: try not to think about what the best practice is, // but rather imagine the simplest solution then ask // "what problems could come up?". // However, in something fundamental (e.g. standard library type // in a low level language boot strapped with asm, used for kernels) // the answer is "everything". } AGGREGATION: • when an object contains another object Composition: • when the containing object manages the lifetime of the contained object • the two lifetimes being tied by any mechanism {stack popping, GC} qualifies as "management" in the trenches "composition" could be used to refer to aggregation; its considered a minor unclarity outside of documentation Inheritance: • a special case of composition when the parent can access non-public members of the child • unless polymorphism is explicitly required, composition is preferred for lighter encapsulation Dependency_injection:"DI" • when the containing object receives the contained object from the outside {constructor} • often preffered as it decouples the containing and contained classes • inherently requires pointers (even if implicit) • some retards refer to functions in general receiving objects as DI, usually in radically OO languages, which, is quite dumb, ie. its just "explicit member functions" for them, except they will never recognize its a member function because there is no syntax support Strategy: Image: // ?!; draw "The tactitian promoting his pawn to a queen." • behaviour is defined as a variable • allows for runtime chaning of behaviour • can prevent messy and seemingly endless branching • can prevent messy inheritance • "The Strategy Pattern defines a family of algorithms, encapsulates each one,\ and makes them interchangeable.\ Strategy lets the algorithm vary independently from clients that use it." — usually acheved by: • function variables { C/C++: function pointer, Python: class 'function'} • composing a polymorphic behaviour class {C++, C#, Java} { # Function strategy diagramm in pseudo-python def fn(...): '' fn = ─ ┬ ─ ─ > def fn_a(...): if a: '' ... ... '' │ return return '' if b: '' ├ ─ ─ > def fn_b(...): ... '' ... return '' │ return if c: '' ... '' └ ─ ─ > def fn_c(...): return '' ... '' return } { // Strategy pattern in C with function pointers /* @BAKE gcc $@ -o $*.out $(pkg-config --cflags --libs ncurses) && ./$*.out */ /* Assume Tyler sends a message to us every 0.5 seconds. * Depending on our inbox settings we can either take it or block it. * The logical model of our application is something like this: * * # User input (Optional) # Message in * | * V * # Receiving strategy * * In the mids of simplicity one could associate to: * * # User input * | * V * # Action * * But please try to refrain from it. */ #include <stdio.h> #include <ncurses.h> // its rather annoying that there is not standard function for *simple* non-blocking input signed main() { /* Irrelevant */ initscr(); cbreak(); curs_set(0); noecho(); nodelay(stdscr, true); scrollok(stdscr, true); /* ---------- */ /* We declare our behaviours, nice and encapsulated. */ void recieve() { addstr("Recieved a message.\n"); } void block() { addstr("A message was blocked.\n"); } /* This is where we store the currently selected behaviour */ void (*strategizing_function)(void) = recieve; while (1) { /* Whatever our current behaviour is, we just execute it. Notice how our user input BELOw is separate from the execution. Triggering the correct action requires no extra logic. In the wild they would not even be in the same function. */ strategizing_function(); /* Allow the user change behaviour at runtime. Here, for any input will just flip it. */ char c = wgetch(stdscr); if (c != EOF) { strategizing_function = (strategizing_function == recieve ? block : recieve); } napms(500); // Message delay } } } { // Strategy examle in C# with polymorphism; // NOTE: try running it before reading it // TODO: ?! somewhere write about how this example highlights the difficulties of not having free functions // @BAKE mcs -out:$*.exe $@ && mono $*.exe using System; using System.Collections.Generic; // --- Stratigized behaviour --- public interface Operation { string description { get; set; } ConsoleColor color { get; set; } void action(); } public class ToDown : Operation { public string description { get; set; } public ConsoleColor color { get; set; } public void action() { int n = Menu.items.FindIndex(o => o.operation == this); if (n != Menu.items.Count-1) { Menu.swap(n, n+1); } else { Menu.swap(0, n); } } } public class ToUp : Operation { public string description { get; set; } public ConsoleColor color { get; set; } public void action() { int n = Menu.items.FindIndex(o => o.operation == this); if (n != 0) { Menu.swap(n-1, n); } else { Menu.swap(n, Menu.items.Count-1); } } } public class ToTop : Operation { public string description { get; set; } public ConsoleColor color { get; set; } public void action() { int n = Menu.items.FindIndex(o => o.operation == this); Menu.swap(0, n); } } // --- Root object --- public class Item { public string name { get; set; } public ConsoleColor color { get; set; } public Operation operation { get; set; } } // --- Demo --- public class Menu { public static List<Item> items = new List<Item>(); public static void swap(int a, int b) { Operation swap = items[a].operation; Menu.items[a].operation = Menu.items[b].operation; Menu.items[b].operation = swap; } public static void print() { foreach (var item in items) { Console.ForegroundColor = item.color; Console.WriteLine($"{item.name}:"); Console.ForegroundColor = item.operation.color; Console.WriteLine($" { item.operation.description}\n"); Console.ResetColor(); } } public static void Main(string[] args) { items.Add(new Item { name = "Item 1", color = ConsoleColor.Yellow, operation = new ToUp { description = "Move up", color = ConsoleColor.Yellow } }); items.Add(new Item { name = "Item 2", color = ConsoleColor.Green, operation = new ToDown { description = "Move down", color = ConsoleColor.Green } }); items.Add(new Item { name = "Item 3", color = ConsoleColor.Blue, operation = new ToTop { description = "Move to top", color = ConsoleColor.Blue } }); while (true) { print(); Console.Write("$ "); string input = Console.ReadLine(); if (string.IsNullOrEmpty(input)) continue; if (int.TryParse(input, out int number)) { if (number > 0 && number <= items.Count) { items[number - 1].operation.action(); } } } } } } . ### The strategy pattern is really bad ### For clarity, I would first like to state that I'm specifically referring to what OOP depicts as THE strategy pattern. If we restrict our selves to the definition of "behaviour as data", procedural function pointers and much of functional programming becomes examples to the strategy pattern. Those work pretty well, I take no offense to their existence. On the other hand, if we take a look at the OOP side, its not all so rosy suddenly. There seems to be multiple reasons to apply the strategy pattern: .=======================. I Reasons to strategize I `=======================` │ │ .------------------. ├──| Trivially needed | │ | a new class | │ `------------------` │ │ .------------------. └──| Cope for lack of | | free functions | `------------------` So, as I was attempting to come up with a simpler example than HFDP's duck hierarchy, I kept coming up with concepts that scream "I AM MY OWN CLASS". For example Magicwands with engraved behaviour, but then there was no way to gaslight the reader that the concept we are arriving to is not a Spell. Then as I applied complexity, I realized the whole dilemma of this design arose from being unable to pinpoint the problem and not it being inherently harder than say prototyping a function. For the second part I wish you to refer to the previously mentioned duck example. However, assuming you are unable to reference the original or dont care enough, here is the quick rundown: • Your current design contains the following hierarchy ┏━━━━━━━━━━━━━━━━━┓ ┃ class Duck ┃ ┣━━━━━━━━━━━━━━━━━┫ │ var duckyness; │ │ void quack(); │ └─────────────────┘ / \ / \ / \ / \ / \ / \ ┏━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━┓ ┃ class MallardDuck ┃ ┃ class ReadHeadDuck ┃ ┣━━━━━━━━━━━━━━━━━━━━┫ ┣━━━━━━━━━━━━━━━━━━━━┫ │ var duckyness; │ │ var duckyness; │ │ void quack(); │ │ void quack(); │ └────────────────────┘ └────────────────────┘ • You are asked to add a RubberDuck • Oh no! rubber ducks cannot quack! they squeak • After juggling terrible ideas such as a quackable interface implemented by the children, Strategies save the day ┏━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━┓ ┃ class Duck ┃ ┃ interface Quack ┃ ┣━━━━━━━━━━━━━━━━━┫ ┣━━━━━━━━━━━━━━━━━┫ │ var duckyness; │ │ void quack(); │ │ var quack; │ └─────────────────┘ └─────────────────┘ | | / | \ | | / | \ | | / | \ | | / | \ | | / | \ | | / | \ | | ┏━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━┓ | | ┃ class MallardDuck ┃ ┃ class ReadHeadDuck ┃ ┃ class RubberDuck ┃ .-----------. .-----------. ┣━━━━━━━━━━━━━━━━━━━━┫ ┣━━━━━━━━━━━━━━━━━━━━┫ ┣━━━━━━━━━━━━━━━━━━━━┫ | class Quack | | class Squek | │ var duckyness; │ │ var duckyness; │ │ var duckyness; │ `---,-------` `-----------` │ var quack; │ │ var quack; │ │ var quack; │- - - - - - - - - - - -' └────────────────────┘ └────────────────────┘ └────────────────────┘ : : : '- - - - - - - - -'- - - - - - - - - - - - - - - - - ' Ok, now lets think about this critically. The quack interface describes a single behaviour that does not require internal state. We used to have those things actually, they were called functions. Seriously, 2 new classes and 1 additional interface is introduced to describe 2 behaviours. Our source got more complex, bloated, dependant -as there are new couplings, never mind how close they are-, and more verbose to extend. If thats not a work around I dont know what is. Digging a little further, does Quack look encapsulated to you? Because to me she seems like a free use whore pretending to be better than her ancestors under the banner of self-expression, but I digress. As a minor note, I would like to poke fun, how without optimizations the ABOVE will be oh-so-slightly slower than the procedural equivalent because now we have to instantiate quacks amongst other things. "But Anon, you ignored rockets being strapped to ducks during the simulation,\ which modifies their fly behaviour" - I hear you say. You are correct. This does mean that we will have to change behaviour at runtime. Which, again, leaves us with scenario 1. Procedurally speaking, having a strategy would be a pattern avoiding strangely placed and convoluted logical blocks, but to replicate the problem in OOP, you would need a hierarchy of strangely placed and convoluted logical blocks, at which point it should be obvious to anyone something very wrong is going on. # Mock: Image: /* ,, !////, !!!////, .|X<////, :\XX<<// :.-''' :.' :.' :.' :.' :.' :.' :.' :.' */ "The broom might not be the best dance partner,\ but is always available for practice." • implementation of an interface • simulates desired behaviour • used for testing and prototyping • can be used to allow for compilation and therefor continue-d work while waiting for a proper implementation • can be used to simulate unlikely and hard to invoke errors Facade: Image: /* _____ .' '. \'-----'/ \ / \ / | | | | | | ‾ */ "The funnel that concentrates the input,\ for higher throughput." • a higher level interface combining one or more lower level interfaces • allows for less verbose expression of complex, but common problems while not polluting the original low-level code { • curl Easy interface (https://curl.se/libcurl/c/curl_easy_init.html) } Proxy: Image: /* +--------------+ |~ ~_----_ | | ~ :_ ##\_ | | ~ o\___ \ | | o'==' | | Yes, Jim! | +--------------+ */ "The commentators talking over the race." • is a block that takes in an input block and returns a compatible block with an existing functionality extended • the interface is not modified • for it to make sense, behaviour must be added • code relying on the input remains unchanged ○ useful examples • stopper • caching • restricting access (like a firewall) Decorator: • a proxy which aggregates, but does not compose the proxied class • commonly used when multiple layers of proxying is desirable — consider the following demented statement from "Head First Design Patterns, 2nd Edition": "Q: Can decorators know about the other decorations in the chain? Say I wanted\ my getDescription() method to print “Whip, Double Mocha” instead of “Mocha,\ Whip, Mocha.” That would require that my outermost decorator know all the\ decorators it is wrapping.\ A: Decorators are meant to add behavior to the object they wrap. When you need to\ peek at multiple layers into the decorator chain, you are starting to push the decorator\ beyond its true intent. Nevertheless, such things are possible. Imagine a\ CondimentPrettyPrint decorator that parses the final decription and can print “Mocha," /* KEY LINE */ "\ Whip, Mocha” as “Whip, Double Mocha.” Note that getDescription() could return an\ ArrayList of descriptions to make this easier." • while that technically works, parsers are not very nice to maintain, the parser has to assume a lot about the output and performace just got executed with a butter knife { #!/bin/python3 # From scratch example in python for decorators # Do NOTE that python also has builtin syntax sugar def print_canary(): print(''' .-. \n''' + ''' /'v'\ \n''' + ''' (/ \) \n''' + '''='="="===<\n''' + '''mrf|_| \n''', end='' ) def print_with_yellow(func): def wrapper(*args, **kwargs): print("\033[33m") r = func() print("\033[0m") return r return wrapper myBirdFunction = print_canary myBirdFunction() myBirdFunction = print_with_yellow(print_canary) myBirdFunction() } { // @BAKE javac $@ && java $* // Java example to the decorator pattern class Decorator { static class Canary { private String ascii = " .-. \n" + " /'v'\\ \n" + " (/ \\) \n" + "='=\"=\"===<\n" + "mrf|_| \n" ; public void print() { System.out.print(ascii); } } static class YellowCanary extends Canary { public void print () { System.out.println("\033[33m"); super.print(); System.out.println("\033[0m"); } } public static void main(String[] args) { Canary sunny = null; sunny = new Canary(); sunny.print(); sunny = new YellowCanary(); sunny.print(); } } } Template: Image: /* /\ / \ / \ .------------------------------. |\/--''--''--''--''--''--''--\/| |/ .~~~~~~~~~~~~~~~~~~~~~~~~.\ | | \| | /| |/ | |\ | | \| | /| |/ | |\ | | \| | /| |/ | |\ | | \'~~~~~~~~~~~~~~~~~~~~~~~~' /| |/\''--''--''--''--''--''--''/\| '------------------------------' */ "The empty frame hanging on the wall;\ the nail is already there,\ you just have to find something that fits." • an algorithm is written without knowing implementation details • the meaning of the operation is know, but the side-effects are not { // Very unhinged C++ template method Disaster dead_end(Entity &e) { Lung l; do { l = e.scream(); } while (not l); return e.next(); } } Generics: • compile time template-s { // C++ min search template <typename C> auto min(const C &iterable) { auto r = std::begin(iterable); for (auto it = std::begin(iterable); it != std::end(iterable); it++) { if (*it < *r) { r = it; } } return *r; } // The type of C is irrelevant as long as it can be // iterated (implements the interface) and whatever // it stores implements the '<' operator. } Adapter: Image: /* _____ +--------------+ ____ .' '. / /| ____| \ | U | *--------------* | (____| `._____ | | | | | ____| _|___ | | | | | Magic | + (____| .' '._____.' | |/ |____/ *--------------* */ "The magic box that sits between two incompatible interfaces." • when an interface is wrapped to be compatible with another • the adapter pattern is could be a sign of technical dept; ask this: are we using an adapter because it makes sense or because we are making cuts? ○ use case examples — good • programming to the abstractions of a library and not the implementation • you have a RATIONAL fear of not touching the internal cryptography class of your organization • 3th party libraries are being wired together — bad • Rajeshwar copy pasted his implementation from another project • "oh wow, so many LOC, i better not touch anything" • writting an adapter-adapter, because the more patterns it has, the better the project gets, right? abstract: • not adapting to new change or preparing for future change, but embracing on demand change • allowing multiple backends // NOTE: this graph assumes we define language syntaxes as interfaces; // nothing about the internals of Bison is assumed .--------. ┌─── | C | │ `--------` │ .--------. ├─── | C++ | │ `--------` │ .--------. +============+ ┏━━━━━━━━━━━┓ ├─── | D | I Programmer I ─── ┃ DSL Bison ┃ │ `--------` *============* ┣━ ━━ ━━ ━━ ┫ │ .--------. │ internals ├─┴─── | Java | └───────────┘ `--------` Observer: Image: /* __________________________ |┌───────────┬────────────┐| |│ │ ______ │| |│ │ (((((()) │| |│ _____ │ /o o |))) │| |│ (.---.)-. │ |/_ |))) │| |│ /:::\ _| │ \, )' │| |│ '-----' ' │ /00\ │| |└───────────┴────────────┘| `````````````````````````` */ "The meddling crazy old woman accross the street\ who keeps calling the authorities." ┏━━━━━━━┓ ┏┛ ┗┓ ┌──────────────┐ ┏┛ ┗.------------->│ Subscriber 1 │ ┃ Publisher/┃ └──────────────┘ ┃ *` ┃ ┌──────────────┐ ┃ *---------------->│ Subscriber 2 │ ┃ *. ┃ └──────────────┘ ┗┓ \┛ ┌──────────────┐ ┗┓ ┏┛'------------->│ Subscriber 3 │ ┗━━━━━━━┛ └──────────────┘ • hierarchical • the publisher is an object that has knowledge of some event, a list of subscribers and the responsibility to notify said subscribers if said event occurs • the subscriber is an arbitrary object or function that should gain execution control if some event has occurred; they obviously must share an interface • a way to subscribe and unsubscribe must exist; classically using methods • subscribers can be added and removed at runtime { /* The switch - publisher * upon being flipped - event * will deliver through * the wire - subscription * the info of its - message * changed state * to the lightbulbs - subscribers * * NOTE: neither the switch knows what its wired to, * nor the bulbs; * they don't have to because they all "know" * how to interface with the wire */ Publisher Subscriber 1 Subscriber 2 ______ ______ __ /O .' '. .' '. b b / / | | | | 'b ^/ / | .--. | | .--. | ' I/ / | { } | | { } | ' C_/I '. `..` .' '. `..` .' ' I I |_ii_| |_ii_| 'P P ;====; ;====; P__P ;====; ;====; │ │ │ └───────────────────────────┴─────────────────────────────┘ /* An implementation my look something like this: */ interface ElectronicInput { void input(bool state); } class Bulb : ElectronicInput { bool state; void input(bool state) { this.state = state; } } class Switch { list<ElectronicInput> subscribers; void on(void) { for (i : subscribers) { i.input(true); } } void off(void) { for (i : subscribers) { i.input(false); } } } /**/ } Signals_and_slots: // ?! • specific implementation of the observer pattern • the naming and concept originates from QT Command: • the idea is encapsulating arbitrary code and state to be executed • behaviour can be changed at run time • somewhat analogous to function pointers { /* We design are designing a * universal remote class. - invoker * We want each button to be * rebindable, keeping that * in mind we create a * generic interface. * This will encapsulate * arbitrary state * required to perform * its purpose. - command * Then something controllable * comes up with an alien * interface. - receiver * We can make new classes * to interact with it. - concrete commands * Finally we wrap it in a * main function that * creates all our objects * and assigns/reassigns * our buttons. - client */ interface Command { void execute(void); } class UniversalRemote { Command button_one; Command button_two; } // --- class PartyLights { void turn(bool state); } // NOTE: we dont have state, but we could class TurnPartyLightsOn : Command { PartyLights light; void execute { light.turn(true); } } class TurnPartyLightsOff : Command { PartyLights light; void execute { light.turn(false); } } } Active_object: • multitasking technique • a command queue is maintained • commands are added to the queue and executed whenever its their turn • protects from hanging Event_sourcing: Image: // ?!; draw "The line of wagons keeping eachother safe\ as they move across the desert." • the act of implicitly storing state as a list of events • the concrete state may or may not be represented directly • the term itself is associated with high importance systems, where any fuck-up would be very costly • highly dependent on the command pattern (see ABOVE) • can act as the poor-mans RPC • can be used to rapidly implement scripting or undo functionality, since objects are easy to record and an Command::execute_reverse() could accompany any Command::reverse() Migration: • a special type of event sourcing, in the context of relational databases • agile technique • basically "how to fix a fucked database"a migration is code, which describes how an existing database should be modified ○ goals • painless schema changes • preserved data integrity • reproducibility on other deployments • one may "bring up", "apply" or "perform" a migration (to the same effect) -- -- Typical minimalistic migration, slightly modifying the status quo -- new requirements specify that each user can be warned once being banned for good ALTER TABLE user ADD COLUMN is_warned BOOLEAN DEFAULT FALSE; the migration could be in any language {raw SQL, PHP with factory syntax sugar, Python class hierarchy} • since migrations only specify change, they must be run sequentially to get an up-to-date version -- -- Migration demonstrating dependence, this migration would fail without the previous example also being run -- on new years eve reset all warn statuses ALTER TABLE user SET is_warned FALSE; • they often contain the definition for the opposite action { CREATE table -> DROP table} — migrations tend to be stored as individual files with clear sequencing { migrations/ ├── 0001_init ├── 0002_add_is_warned_column └── 0003_reset_is_warned_column } • the current state of migrations is always recorded { // `$ php artisan migrate:status` output from a dummy project Migration name ............................................................. Batch / Status 2014_10_12_000000_create_users_table .............................................. [1] Ran 2014_10_12_100000_create_password_reset_tokens_table .............................. [1] Ran 2019_08_19_000000_create_failed_jobs_table ........................................ [1] Ran 2019_12_14_000001_create_personal_access_tokens_table ............................. [1] Ran 2023_12_09_145206_init ............................................................ Pending } • (2023) they tend to be abstracted in awfully framework specific ways and be buggy as fuck, with broken drops, corrupted fields and state stalemates; my advice is to only ever touch them lightly and never try looking under the hood; with errors encountered, you are better of restarting with a clean state seeder: • a special migration which only deals with adding data Factory: Image: /* __ . .* , ~#@#%(" .,$ @ ."^ ';" .. ;. : ;==: ;. : ;==:_______. _;. : ####:__. _/o;==: ####: : _____ ___/o/';. ; : :__ | |b. (o o/__.' ;.AA....:..:.. o'''o''' */ "The factory, parts go in on a conveyor belt and items come out,\ but their internals and the assembly is hidden." • a central point of logic controlling polymorphic construction • the appropriate subclass is deducted from the parameters passed • encapsulates subclasses, decoupling it from the code using them • a back-swing of the encapsulation inheritence has violated Factory_class: • dedicated class that is used as a factory • in the context OOP this is what "just" factory referes to • usually the class name will contain the word Factory • the concepts of factory function && factory method follow exactly the same logic Abstract_factory:"kit" • polymorphism applied to factories { // Assume we have a platformer game with a tile based map // There are solid tiles, jump through tiles and tiles which // get destroyed after a brief period of the player standing on them. // Their creation is done by a factory, however, based on the // type of map biome we are on they must act slightly differently. // E.g. different textures, sounds effects, particles and self-destruction times. ┏━━━━━━━━━━━━━━━━━━━━┓ ┃ interface ┃ ┃ GameTileFactory ┃ ┣━━━━━━━━━━━━━━━━━━━━┫ │ tile CreateCube(); │ └────────────────────┘ / \ / \ / \ ┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ class ┃ ┃ class ┃ ┃ ForestLevelTileFactory ┃ ┃ DesertLevelTileFactory ┃ ┣━━━━━━━━━━━━━━━━━━━━━━━━┫ ┣━━━━━━━━━━━━━━━━━━━━━━━━┫ │ tile CreateCube(); │ │ tile CreateCube(); │ └────────────────────────┘ └────────────────────────┘ } Visitor: Image: TODO ?! { class AA : public A { void accept(Visitor v) { v.AA(); } }; } " \ The Visitor Pattern is very important in general, but \ it’s beyond ubiquitous for us compiler developers. It \ helps avoid bloating AST node classes with methods and \ state required for the various operations we perform on \ them. It also often saves us from writing AST traversal \ code. \ " • polimorphism/overloading, but weirder • we wish to use polymorphism, with methods that can only access public fields; this problem has no language support what so ever • a Visintor class/interface is created, which has the desired outsider methods for our subclasses and a function which calls an accept method which allows instances to "chose" which is the correct method { foreach e : el { x v.visit(e │ } │ │ <-- "Double dispatch" │ | | │ V V │ /*==================*//* Visitor *//*==================*/ └────▶| visit(e){ | /*============*/ | e.accept(this) |x ─ ─ ─ ┐ /* a */ | } | /*============*/ | | ├ ─ ─ ▶| accept(v){ | | on_a(e){ |◀─────────────x| v.on_a() | | (a)e; | │ | } | | } | +------------+ | | │ | on_b(e){ |◀────┐ /*============*/ | (b)e; | │ │ /* b */ | } | │ /*============*/ +------------------+ │ └ ─ ─ ▶| accept(v){ | └────────x| v.on_b() | | } | +------------+ } • we add 2 redundant function calls to each 1 we actually wanted, we add a new interface on which our hierarchy depends on, on the process the original hierarchy is modified and the new class closely couples with our hierarchy • i get the problem, i really do, but this is insanity; one of those "self proposed problems" — other ways this could be solved: • have language support for methods which can only access private fields • use reflections in some (any) way to figure out the overloading • do not employ morons who fuck up everything they touch • the cool thing about this is encapsulation, right? we may have an {Exporter} visitor which is clearly cut from the base, preventing it from getting helplessly bloated, however the double dispatch model is the ugliest thing i've seen; its clearly a work around over a new sort of problem Builder: Image: /* _______ .````````. |`. `. |'. _______`. |˙.|`````| |'.| | ________|__| [#] |________|__| op] |___________ ( \ \ | | \ \ | [oOO] | \ \ \ \ \____\___| } : |____\____\_| \/ |___\____\___\ \(O O | | O O O | ~= |O O O O) ‾‾‾‾‾‾‾‾|#####|‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾‾‾ */ "The production line, you set up your machines then\ whatever you place onto the belt, it comes out on the other side.\ Processing is guaranteed, you only need to judge the quality of the end product." • a class whichs purpose is to aid contruct object of another class • construction is broken up into multiple steps represented as methods • the function which finally returns the actual instance is usually called .build() • every member which is used for construction returns the builder instance, to allow method call chaining • construction values not provided by the developer are usually (hopefully) set to sensible defaults • can be regarded as very primitive DSLs • a monad concerned with construction • useful when not all construction options are available immidiately, as the builder can be used for storing partial options temporaliy • may or may not perform extra operations other than calling the constructor { // The implementation is very boring and obvious, // you can do it youself without further help. // Call to a builder: Human enemy_wizard = HumanBuilder // actual constructor call, // everything below modifies the object, // but the caller doesnt have to know this // if the values were faulty {negative range {5,1}} // the rest would be skipped over by checking // for a stored error state // and a null value would be returned by build() .random_level_between(3, 10) .add_random_n_items(10) .add_random_armour(ARMOUR_RARE) .add_random_potion_effect() // stop returning a reference // to the builder object .build() ; // NOTE: look at how nice and clean it looks; // peak readability, peak editability, // no footguns } Inversion_of_control: Image: /* Ruth Ginsberg __ / \ / ..|\ (_\ |_) / \@' / \ _ / ` | \\/ \ | _\ \ /_ || \\_ \____)|_) \_) */ "The guide dog. He is under your command,\ yet he is the one leading the way""IoC" • giving 3th party code {framework, DSL, library} control over our application — hollywood princaple: • "Don't call us, we will call you." • catchy reminder to "IoC exists, and if you are using it,\ dont fuck it up by closely coupling your handlers" traditional_sense: • the control flow is inverted • event or state driven { // Flex is inverts the control %% /* Subscription to the event of reading 'a'; * we gain control briefly, but then its returned back. */ a { ECHO; } /* Whats important to realize is that we have barely any * bloody clue what lex does between our handlers, * or, the very least, we don't have to know. */ .|\n { ; } %% signed main(void) { /* Actual inversion below. * In this case we explicitly ask for it * and could do whatever before hand, * (while untypical in C) * it would be possible that the framework * inverts the control by default. */ yylex(); } /* Everything relevant to what our program * actually does is present ABOVE. * Pretend for a second that Lex is an internal tool, * it still saved us from reading through a bunch of * insignificant implementation details. */ /* Other examples: * >tbsp * >libirc * >browser javascript */ } java_sense: alternative meaning that was conceived by OOP and Java • the responsibility of creating ancestors is delegated away • leverages dependency injection • saves you from creating a bunch of objects by hand • only comes up when a framework is a cluster-fuck anyways, so its safe to call it shit Honourable_mentions: • the following are too obvious and or stupid to have their own sections Bridge: +---+ +---+ | a |-. .-| а | +---+ '. .' +---+ '. .' +---+ '.+-----------+ +-----------+.' +---+ | b |--------| interface |---| interface |--------| б | +---+ .'+-----------+ +-----------+'. +---+ .' '. +---+ .' '. +---+ | c |-' '-| в | +---+ +---+ • abstraction depends on abstraction Monostate: • two code segments sharing state using references — so generic that its meaningless: • virtual tables are monostate • class static is monostate • shared pointer is monostate • variable reference is monostate • when combined with singleton, it is the text-book example of reinventing global state State: Automaton Flex • state automaton applies practically • easy to debug • easy to visualize • easy to understand • great for parsing (might be only a component) • great for drawing conclusions from many conditionals Composite: not to be confused compounds • a container that redefines operator-s to apply to all elements { # Composite in Python/numpy import numpy as np array = np.array([1.5, 2.7, 3.9, 4.4, 5.8]) array = array / 2 print(array) # [0.75 1.35 1.95 2.2 2.9] # NOTE: dividing a matrix by a number is formally defined # as dividing each element } /* performance:: ___ __ | _ \___ _ _ / _|___ _ _ _ __ __ _ _ _ __ ___ | _/ -_) '_| _/ _ \ '_| ' \/ _` | ' \/ _/ -_) |_| \___|_| |_| \___/_| |_|_|_\__,_|_||_\__\___| */ Prototype: • a so called "prototype" object is used to initialize further objects, using memory copying • useful when the computations of the initialization would be costly; but can be done ahead of time Caching: Memoization: • argument based caching for pure functions Multi_processing: • strictly outsourced to the kernel Thread: • abstraction over a physical core • can be software emulated by time sharing • the number of threads do not depend on the number of cores • every thread can access the resources of the main thread • each thread has its own stack Spinlock: { /* Waiting thread, | Waited thread * with spinlock | */ | while(i) { ; } | i = false; } /* compounds:: _____ _ / __ \ | | | / \/ ___ _ __ ___ _ __ ___ _ _ _ __ __| |___ | | / _ \| '_ ` _ \| '_ \ / _ \| | | | '_ \ / _` / __| | \__/\ (_) | | | | | | |_) | (_) | |_| | | | | (_| \__ \ \____/\___/|_| |_| |_| .__/ \___/ \__,_|_| |_|\__,_|___/ | | |_| */ • a pattern of patterns MVC:"Model-View-Controller" • used for UIs { Controller (UserInput) { switch (UserInput.Type) { case A: return ModelA(UserInput.Data); case b: return ModelA(UserInput.Data); // ... } } ModelA (UserData) { ProcessedData = process(UserData); return ViewX(ProcessedData); } ViewX (ProcessedData) { return f"I: {ProcessedData.i}, " f"H: {ProcessedData.h};" } } ┌───────┐ .-│ Model │- .' └───────┘ '. | | | | ┌──────┐ ┌────────────┐ │ View │ │ Controller │ └──────┘ └────────────┘ \ / \ / \ / \ / +------+ | user | +------+ — MVP: • "Model-View-Presenter" • an intelectual blackhole • you can find the MVC graph online with all possible permutations of arrows; mostly because the view gets associated with the literal interface the user is presented with, and not what procudes that using data • MVP is a "clarification" regarding the controversy that webdevs caused; because they cannot abstract and had breakfast yesterday