Библиотека программиста

Это список статей о концепциях, которые я считаю важным для любого разработчика. Он составлен в порядке от самых базовых структурных концепций до высокоуровневых концепций и включает лучшие практики (best practices), принципы и списки статей, доклады на конференциях и книги по разработке программного обеспечения.

Страница постоянно обновляется.

  • Good Practices

    • Reflect the architecture in the code folder structure

      Doing structural encapsulation makes it a lot easier to communicate bounded contexts, dependencies,
      layers, related functionality and purpose of grouped classes. Close proximity in the tree
      suggests closer coupling. The top level should reflect the software usage, so it should be named
      according to the Domain, but in broad encapsulated concepts, because as business evolve the domain
      details might change. [Mathias Verraes, Robert C. Martin]

      Make the architecture predictable and readable. Once you’ve agreed on an overall architectural
      structure for your project, make it obvious where the main elements are located. It will reduce time
      wasted, keep consistency, prevent lost code and avoid unaware code duplication. [Christian Maioli]

    • Clean Code
      • Make the code predictable and readable
        Don’t code “your way”. Just follow your team coding standards. Make your code predictable and
        easy to read by coding the way people expect. [Christian Maioli]
      • Create small, focused and encapsulated code units
        The approach when breaking a problem down should be to have each section as focused as possible, affecting only local state, without mixing in irrelevant issues, and without side-effects if at
        all possible. [Christian Maioli]
      • Make it discrete and processable
        Using magic strings, magic array indexes and custom template language features will lead to a
        more disconnected codebase. [Christian Maioli]
      • Don’t use Magic Numbers. Unique values with unexplained meaning or multiple occurrences which could (preferably) be replaced with named constants [medium.com]
    • Conditionals (if, case)
      • Avoid nested IF statements [Christian Maioli]. Deeply nested conditionals make it just about impossible to tell what code will run, or when. [source]
      • Avoid ELSE statements [Christian Maioli]
      • Encapsulate the IF condition in a meaningfully named method [Christian Maioli]
      • Blocks inside conditionals should be one line long, a function call which, if named correctly, adds documentary value [Robert C. Martin]
      • Do the exclusionary IFs first, and do early returns from those conditions. That leaves the bulk of the method body to do the expected work. [Matthew W. O’Phinney]
      • Replace conditionals by polymorphism, when you have a conditional that chooses different behavior depending on the type of object.
        Using inheritance: Extract the different behaviors into different subclasses and make the original method abstract. [Misko Hevery]
        Using composition: Extract the different behaviors into different classes and inject the one you want in the original class. [Sandi Metz]
    • Loops
      • Blocks inside loops should be one line long, a function call which, if named correctly, adds documentary value [Robert C. Martin]
    • Naming conventions
      • Name your variables according to the class they contain [Mathias Verraes]
      • Name functions/methods using a verb, according to their context and what they do
        Document the why. Relevant and contextual variable and function names are the way to do this.[Robert C. Martin, Christian Maioli]
      • Name your classes using a noun, according to their domain and architectural meaning [Robert C. Martin]
      • A class name should start by its domain meaning (ie. User) and be postfixed by their architectural meaning (ie. Factory) like UserFactory.
      • A class name should identify it without ambiguity
        Name your classes with a name as unique as possible, regardless of their namespace, so we don’t have to lose our focus from the code to go check the namespace.
    • Functions / methods
      • Should be as small as possible [Robert C. Martin]
      • Should have an explicit intent, both in its name and code [Robert C. Martin]
      • Should do one thing only and do it well. One thing means one level of  abstraction (manipulating strings is one level, manipulating business rules is another level), or when nothing else can be extracted and meaningfully reused in another function or method [Robert C. Martin]
      • Methods in a class should be organized per level of abstraction, where the methods used by a method sit directly below it [Robert C.  Martin]
      • Choose descriptive names, for small functions, that do one thing [Robert C. Martin]
      • Should have at most 3 arguments, if we have more than 3 and they are conceptually related, we should group them in an object [Robert C. Martin]
      • Do not use output-arguments (arguments to output data out of a function/method) [Robert C. Martin]
      • Don’t use boolean arguments
        • use 2 functions/methods instead [Robert C. Martin]
        • A boolean parameter will most likely be a logic switch for the method logic. This means it is
          actually hiding 2 methods. Then its better to actually have two methods. [Marco Pivetta,
          Robert C. Martin]
        • Flags are not a best practice because they reduce the cohesion of a function. It does more than one thing.
        • Booleans make it hard to understand what a function does based on the call site. [source]
      • Comply to CQS, either:
        – Do something (change state)
        – Get something (return info about an object) Not both [Robert C.Martin]
      • Don’t return an error code, throw an exception instead [Robert C. Martin, Misko Hevery]
    • Classes
      • Named constructors
        Its OK to use them. [Mathias Verraes]
      • Static methods
        Use them when they are stateless and generic only (not domain, so we can have different
        implementations of domain logic injected in different places)  [Mathias Verraes]
      • Private members access between sibling objects
        It is handy, and OK, to do so in restricted situations, like comparing sibling objects properties.  [Mathias Verraes]
      • Don’t set state by reference
        All state should be encapsulated and inaccessible from outside the object. If we set a property
        using a given reference, the given object is still accessible from outside. If needed, clone the
        object being set; [Marco Pivetta]
      • Don’t use setters
        Change in an object state should be the consequence of a conceptual/domain operation applied and encapsulated in the object. The constructor should be the only state injection point. If a
        business logic only sets a data value, so be it, but don’t name the method as a setter, name it
        by the business logic it reflects; [Mark Ragazzo, Marco Pivetta,
        TellDontAsk]
      • Avoid Getters
        Using getters is sometimes necessary, but most of the times they should not be necessary. Their
        mere existence encourages putting business logic outside the class that has the data necessary
        to fulfill that logic, breaking encapsulation; [TellDontAsk]
      • Declare class as final
        So it can’t inadvertently break LSP; [Mark Ragazzo, Marco Pivetta]
      • No mixed argument types
        Don’t use an array/collection with different types inside as a parameter; [Marco Pivetta]
      • No mixed return types
        Don’t return an array/collection with different types inside. If needed, use a value object to
        encapsulate the different types; [Marco Pivetta]
      • NULL objects:
        As an argument
        When a class has an optional dependency on a service, it is common practice to initialize it
        with NULL. However, it might be more readable and less error prone to initialize that dependency
        with an object that fulfills the dependency but all its methods just do nothing. This makes it
        possible to not need to check if the dependency is set and will make sure
        no errors will occur if someone tries to use a non set dependency; [qafoo.com]
        As a return value
        Never return null, return a null object or empty list instead, to reduce the
        conditionals needed by the client code; [Misko Hevery, Sandi Metz]
      • Composition over inheritance [Sandi Metz]
    • Immutable objects [Mark Ragazzo, Marco Pivetta]
      • When you need to modify an immutable object we actually return a new object
        with the new state;
      • Declare properties as private so state can’t be modified from outside the
        object, nor even extending classes;
      • Do not store references to mutable objects or mutable collections, in an
        immutable object. If we store a collection inside an immutable object, it should be immutable
        too, including its size, its elements and content of its elements;
      • Declare class as final
        So it can’t be overridden adding methods that modify internal state;
    • Dependencies
      • All services should get all their dependencies and configuration values injected as constructor arguments. Following this rule ensures that you’ll never fetch a service ad hoc, e.g. by using Container::get(UserRepository::class). This is needed for framework decoupling because the global static facility that returns the service for you is by definition framework-specific. The same is true for fetching configuration values (e.g. Config::get(’email.default_sender’)). [Matthias Noback]
      • When a dependency uses IO (the database, the filesystem, etc.), you have to introduce an abstraction for it. If you depend on a concrete class, that class will work only with the specific library or framework that you’re working with right now, so in order to stay decoupled you should use your own abstraction, combined with an implementation that uses your current library/framework. [Matthias Noback]
      • Other types of objects shouldn’t have service responsibilities. [Matthias Noback]
      • Contextual information should always be passed as method arguments. Instead of fetching this data whenever you need it (like Auth::getUser()->getId()) you should pass it from method to method as a regular argument. [Matthias Noback]
    • Entities
      • They are the same if their ID is the same; [Mathias Verraes]
      • Don’t create/use anemic entities
        Entities should contain logic that manipulates their instances data, inside a specific object
        and between objects of the same type. However, they should not have knowledge about higher level domain processes. This is a fine line, easy to miss, and involves heavy ponderation and
        contextualization into the domain concepts; [Ross Tuck]
    • Value objects [Marco Pivetta, Mathias Verraes]
      • They are the same if their state is the same;
      • They ensure consistency, because the data is encapsulated and
        immutable.
      • They simplify data validation, because we don’t need to validate their
        data everywhere its needed (its validated when creating the value object);
      • They encapsulate state and behavior (including validation);
    • Exception Handling
    • Business Rules
      • BRs applicable to a Domain Object must be enforced by the object itself. For
        example, if a Costumer must always have an email, make it impossible to create a Customer entity
        without an email, by forcing its injection in the constructor; [Mathias
        Verraes
        ]
      • If a BR should be enforced in a domain object sometimes and sometimes not,
        maybe we are missing some relevant domain concept, maybe we should be using two
        different domain objects
        instead. For example, if a customer sometimes doesn’t need
        an email and sometimes must have an email, maybe we should have a ProspectiveCustomer
        and a PayingCustomer entities; [Mathias Verraes]
      • If the verification of a BR needs more than one object (ie an entity and a repository)
        encapsulate it in a SPECIFICATION object.
      • When using a specification object to model a BR, encapsulate its different
        representations in the same specification object
        (ie code and SQL representations); [Mathias
        Verraes
        ]
      • When using a specification object, test the different representations of the BR by
        comparing their result
        against each other; [Mathias
        Verraes
        ]
    • Modeling the domain
      • Start modeling the domain by defining the use cases (ie
        addProductToBasket) and from there decide what commands, handlers, services, entities,
        value objects and repositories are needed; [Mathias
        Verraes
        ]
    • Talks
  • Principles

    • DRYDon’t Repeat Yourself
    • KISSKeep It Simple Stupid
    • Law of Demeter
      A component or object should not know about internal details of other components or objects
      (Encapsulation).
    • YAGNIYou Ain’t Gonna Need It
      If some code, methods, classes are not needed now, don’t leave them there just in case they are needed
      in the future. Not even if they are commented out. Who knows how it will look in the future, we might
      end up spending time updating code that is not used, we might never need it, we can always rebuild it or
      get it from the VCS.
    • TellDontAsk [Martin Fowler, Don’t use setters, Avoid getters]
      Instead of having a class with getters and setters and call those methods to perform some business
      logic, we should encapsulate that business logic inside the class that has the data and create a method
      named after the business logic we are implementing.
    • STUPID [nikic]
      • Singleton
      • Tight coupling
      • Untestability
      • Premature Optimization
      • Indescriptive Naming
      • Duplication
    • SOLID – Robert C. Martin
    • Package Cohesion Principles – Robert C. Martin
    • Package Coupling Principles – Robert C. Martin
    • Inversion of Control
    • GRASP – “General Responsibility Assignment Software Patterns”
      • Easy reading: Wikipedia
      • Complete reading: Carnegie Mellon School of Computer Science
      • My own short descriptions:
        • Controller
          Controller pattern
          A controller is defined as the first object beyond the UI layer that receives and coordinates (“controls”) a use case scenario.
        • Creator
          Factory method, Abstract Factory and Builder patterns
          In general, a class B should be responsible for creating instances of class A if one, or preferably more, of the following apply:
          – Instances of B contain or compositely aggregate instances of A
          – Instances of B record instances of A
          – Instances of B closely use instances of A
          – Instances of B have the initializing information for instances of A and pass it on creation.
        • High Cohesion
          The responsibilities of a given element are strongly related and highly focused. Relates to the SOLID Single Responsibility Principle.
        • Indirection
          Mediator pattern
          Increase low coupling by using a Mediator class to coordinate the interactions between two objects.
        • Information Expert
          Encapsulation
          Place a given responsibility on the class with the most information required to fulfill it.
        • Low Coupling
          Low coupling is an evaluative pattern that dictates how to assign responsibilities to support:
          – lower dependency between the classes,
          – change in one class having lower impact on other classes,
          – higher reuse potential.
        • Polymorphism
          Usage of inheritance, abstracts, interfaces, generics, overloading and overriding.
        • Protected Variations
          Bridge, adapter and similar patterns
          Protect elements from the variations on other elements (objects, systems, subsystems) by
          wrapping the focus of instability with an interface and using polymorphism to create
          various implementations of this interface.
        • Pure Fabrication
          Service class
          Use Service classes, who do not represent a concept in the problem domain, specially
          made up to achieve low coupling, high cohesion, and reusability.
    • Внедрение зависимости (Dependency injection, DI)
      • Какое главное отличие Dependency Injection от Service Locator? [habr.com]
    • Talks

_________

via: https://herbertograca.com/dev-theory-articles-listing/

Alexey Fedko

Share
Published by
Alexey Fedko
Tags: Agile architecture DDD design patterns dev exceptions Functional Programming microservices OOP php principles REST SOLID Team Building Technical leadership testing

Recent Posts