Nearly two years for system design, whether it is direct use of traditional design ER diagrams, or use 4 c modeling, but when doing architecture review, ER are top priority, let a person have to ponder, generations of development after the programming thought, why still around ER, in ancient times, there is no OO, DDD, but why the great software also abound continues today

With that in mind, we need to go back and say, why can’t structured programming work? Why did object orientation come into being, and what problems did it solve?

The Way to Clean Architecture also has a special introduction to object-oriented programming. What is object-oriented, mostly from three features: encapsulation, inheritance, and abstraction, but these three features are not unique to object-oriented languages

Structured programming

When we think of structured programming, we naturally think of sequential structures: code executes in the order it was written, with a selection structure: if/else, and a loop structure: do/while

While these are familiar to every programmer, in between structured programming there is unstructured programming, the goto statement era, where there is no if else, while, everything is controlled by goto statements, which allow programs to run anywhere, which makes it almost impossible to maintain as the code gets larger

Programming is a difficult activity. Because a program can contain so much detail, far beyond a person’s cognitive ability, any small error can cause the entire program to fail. So big problem needs to be split into small problems, the recursive step by step, in this way, a big problem can be decomposed into a series of the combination of advanced function, and these advanced functions respectively and then split into a series of low level function, split step by step, each function need to be carried out in accordance with the structured programming development, which is now commonly used way of development module function decomposition

In structured programming, the dependencies of modules are too strong to be effectively separated. Once the requirements change, the whole system will be connected, and the related modules will have to change due to the dependencies, so organizing large-scale programs is not its strong point

object-oriented

Because of the disadvantages of structured programming, so with object-oriented programming, we can better organize the program, relative to the structure of the local thinking, we have a more macro perspective: objects

encapsulation

Circle a set of associated data and functions so that code outside the circle can only see part of the function and the data is completely invisible; Such as public functions and private member variables in a class

Extract the keywords:

  1. Data, completely invisible
  2. Function, you can only see
  3. associated

These seem to be the high cohesion, often referred to as the congestion model, in which the most basic encapsulation is not achieved in practice

Anaemic models are everywhere, but the whole is divided into two parts: a Godlike service full of big methods and a model with only getters and setters

Service provides interfaces, model transfers data, database solidifies data, which has no encapsulation, behavior and data separation

How do you achieve a highly cohesive encapsulation feature?

When designing a class, consider what behavior its objects should provide. We then provide methods based on these behaviors, and finally consider what fields to use to implement these methods

And provide getters and setters for these fields as little as possible, especially setters

Exposing getters and setters exposes implementation details. Second, data is at the heart of the design

The name of the method is what you want it to be, not what you want it to be

Public void setPassword(final String password) {this.password = password; Public void changePassword(final String password) {this.password = password; }Copy the code

Changing the setter to a specific business method name, embodiments the intent, and separates the intent from the implementation, is an essential consideration of good design

To build a cohesive unit, we want to reduce the exposure of the unit, i.e., the “visible function” in the definition.

The first meaning of this sentence is to reduce the exposure of internal implementation details. It also has the second meaning of reducing the exposure of interfaces

Minimize interface exposure. That is, every time you add an interface, you need to find a good reason.

Summary: Behavior-based encapsulation, do not expose implementation details, and minimize interface exposure

inheritance

Let’s start with the inheritance definition:

Inheritance is a concept in object-oriented software technology. This technique makes it easy to reuse previous code, which can greatly shorten development cycles and reduce development costs

Inheritance means that a subclass inherits the characteristics and behaviors of its parent class, so that a subclass object (instance) has the properties and methods of its parent class, or a subclass inherits methods from its parent class, so that a subclass has the same behaviors as its parent class

Inheritance, by definition, is about reuse, putting some common code into the parent class, and then implementing the subclass, you can write less code, eliminate duplication, code reuse

There are two types of inheritance: implementation inheritance and interface inheritance

Child object = new Child();

Parent object = new Child();
Copy the code

But there is a design principle: comorbation-over-inheritance is better than comorbation-over-inheritance

Why not use inheritance?

Inheritance means strong coupling, and high cohesion and low coupling are good for our trace, but that doesn’t mean you can’t use inheritance. You need composition for behavior, and inheritance for data

Inheritance also violates OCP in SOLID. Inheritance can extend new behavior through a subclass, but because a subclass may depend directly on its parent, a change may affect all subclasses. Although inheritance can be Open for extension, it is difficult to be Closed for modification

To borrow an example from Daniel Ali:

There is a game where the basic rule is that you equip weapons to attack monsters

  • Players can be Fighter, Mage, Dragoon
  • A Monster can be an Orc, Elf, or Dragon. A Monster has health
  • A Weapon can be a Sword, a Staff, and a Weapon can attack
  • The player can equip a weapon. The weapon attack can be physical (0), fire (1), ice (2), etc. The weapon type determines the damage type
public abstract class Player {
      Weapon weapon
}
public class Fighter extends Player {}
public class Mage extends Player {}
public class Dragoon extends Player {}

public abstract class Weapon {
    int damage;
    int damageType; // 0 - physical, 1 - fire, 2 - ice etc.
}
public Sword extends Weapon {}
public Staff extends Weapon {}
Copy the code

The attack rules are as follows:

  • Orcs deal half of their physical damage
  • Faerie damage to magic attacks halved
  • Dragons are immune to physical and magical attacks unless the player is a dragoon, which doubles the damage
public class Player { public void attack(Monster monster) { monster.receiveDamageBy(weapon, this); } } public class Monster { public void receiveDamageBy(Weapon weapon, Player player) { this.health -= weapon.getDamage(); }} public class Orc extends Monster {@override public void receiveDamageBy(Weapon Weapon, Player player) { if (weapon.getDamageType() == 0) { this.setHealth(this.getHealth() - weapon.getDamage() / 2); // Orc physical defense rules} else {super.receiveDamageby (weapon, player); } } } public class Dragon extends Monster { @Override public void receiveDamageBy(Weapon weapon, Player player) { if (player instanceof Dragoon) { this.setHealth(this.getHealth() - weapon.getDamage() * 2); // Else no damage, dragon immunity rule}}Copy the code

If, at this point, you want to add a weapon type: the sniper rifle, which can ignore all defenses, this needs to be modified

  1. Weapon, extended sniper Gun
  2. Player and all subclasses (can you equip a weapon)
  3. Monster and all subclasses (Damage calculation logic)
public class Monster { public void receiveDamageBy(Weapon weapon, Player player) { this.health -= weapon.getDamage(); If (Weapon instanceof Gun) {// new logic this.sethealth (0); } } } public class Dragon extends Monster { public void receiveDamageBy(Weapon weapon, Player Player) {if (Weapon instanceof Gun) {// New logic super.receivedamageby (Weapon, Player); } // Omit the old logic}}Copy the code

Therefore, add a rule, almost all classes have to modify it again on the link, the more complex the later business, every business basic to rewrite a requirement change, and that is why suggest try not to violate the OCP, the core of the reason is that the existing logic changes may affect some of the original code, cause some unforeseen effects. This risk can only be guaranteed through full unit test coverage, but it is difficult to guarantee UT coverage in real development

It also shows that inheritance is really not a good way to reuse code

From a design principle perspective, inheritance is not a good way to reuse; Nor is it encouraged by language features. One is that Java can only be inherited solely, and once it is inherited, it cannot be inherited by others, and There is the limitation of Variable Hiding in Java

For example, now add a business rule:

  • A warrior can only be armed with a sword
  • A mage can only equip a staff
@Data public class Fighter extends Player { private Sword weapon; } @Test public void testEquip() { Fighter fighter = new Fighter("Hero"); Sword sword = new Sword("Sword", 10); fighter.setWeapon(sword); Staff staff = new Staff("Staff", 10); fighter.setWeapon(staff); assertThat(fighter.getWeapon()).isInstanceOf(Staff.class); // Error}Copy the code

Weapon = weapon; weapon = weapon; The strong typing of this programming language does not carry business rules.

Inheritance is not the only way to reuse, as there are mixins in Ruby

polymorphism

Polymorphism literally means “multiple states.” In object-oriented languages, the various implementations of interfaces are known as polymorphisms

Last time, interface inheritance was more of a polymorphic feature

Only the use of encapsulation and inheritance of programming, called object-based programming, and only the introduction of polymorphism, can be called object-oriented programming, object-based and object-oriented programming, with polymorphism, can distinguish object-based and object-oriented; With polymorphism comes greater flexibility in software design

Polymorphism is great, but to use polymorphism, you have to construct an abstraction, and to construct an abstraction you have to figure out what different things have in common, and that’s the most challenging part. Interfaces play an important role in the construction of abstractions: an interface separates the changing part from the invariant part. Interfaces are conventions; conventions are invariable; what changes are their respective implementations. Interface is a boundary, communication protocol is the important communication between system modules, and interface is the expression of communication protocol

ArrayList<> list = new ArrayList();

List<> list = new ArrayList();
Copy the code

The difference between the two is the type of the variable, which is oriented to an interface or a concrete implementation class; It doesn’t seem to make sense, but as you can see in SOLID, almost all of the principles need to be programmed based on an interface, right

And that’s the power of polymorphism

In the case of Java, inheritance and polymorphism are interdependent, but not in other languages

conclusion

In addition to structured programming and object oriented programming, there are still functional programming, but through the above elaboration, back to the first question, I should be got mixed up on the programming language and programming paradigm, as structured programming, object oriented programming is a programming paradigm, and specific C, Java is a programming language, for programming language is young, It did come after a lot of great software, but the programming paradigm has always been there, and the object-oriented paradigm didn’t come after Java

It’s not that C language can’t create great software, language is just a tool, and the most important thing is the way of thinking. Recently, when I think about why TDD, DDD and other driven development are difficult, the key is the way of thinking

Why do we need to look at ER diagrams, which are often confused with the concept of data model and domain model, which will be decomposed in the next article

Reference

How to Organize Clean

The Beauty of Software