This is the 4th day of my participation in the August More Text Challenge

What are design patterns

What are design patterns? Design patterns are a set of repeatedly used, widely known, cataloged experiences of elite code designers

Why use design patterns? Design patterns are used to make code reusable (with little or no modification to solve new problems), reduce rework, make code easier to understand, and ensure code reliability.

Design pattern is a kind of middle layer surface in the process of programming skills or experiences (micro level such as algorithms, data structure, grammar, macro level such as framework, toolbox, system architecture), many good programmers is summed up by a large number of actual project hone advocates “secrets”, by studying their efficient methods, sums up the experience of skills, Then when we encounter a specific problem, it is easier to come up with a good and reasonable solution.

What is bridge mode

Bridge Pattern is a structural design Pattern, which separates the abstract part from its implementation part so that they can change independently.

  • Abstraction (Abstract class) : Interface used to define the abstract class and maintain a pointer to the Implementor class. It has an association with Implementor (native dot.java).

  • RefinedAbstraction: Extends the interface defined by Abstraction in which the business method defined in Implementor can be called (spheredot.java in this case).

  • Implementor: Defines an interface that implements the class. This interface does not have to be exactly the same as the interface which is used in Abstraction, in fact the two interfaces can be completely different (iease.java in this case).

  • ConcreteImplementor: An interface that implements the Implementor definition and provides different implementations of the basic operation in different ConcreteImplementors. When the program runs, the ConcreteImplementor replaces its parent to the Abstraction concrete business operation method (the various slow classes in this case).

Concrete examples and code comments

Bridge interface definition iease.java

interface IEase {
    float ease(float x);
}
Copy the code

Then there are some definitions of the easing function classes (*.java)

import processing.core.*;

public class EasingEaseInOutCubic implements IEase {

  static PApplet app;

  public EasingEaseInOutCubic(PApplet papp) {
    app = papp;
  }

  public float ease(float x) {
    if (x < 0.5 f)
      return 0.5 f * app.pow(2f * x, 3f);
    else
      return 0.5 f * app.pow(2f * (x - 1f), 3f) + 1f; }}Copy the code

import processing.core.*;

public class EasingEaseInOutQuint implements IEase {

  static PApplet app;

  public EasingEaseInOutQuint(PApplet papp) {
    app = papp;
  }

  public float ease(float x) {
    return x<.5f ? 16f*x*x*x*x*x : 1f+16f*(--x)*x*x*x*x; }}Copy the code

Abstract the Dot class definition. Note that iease, a member of the Bridge, is responsible for bridging concrete easing functions when called

public abstract class Dot {

  static PApplet app;

  float center;
  PVector pos;
  float velocity;
  float counter;
  float size;
  float step;
  float ease;
  
  boolean organize;
  
  /// @note Bridge
  IEase iease;

  Dot(PApplet papp) {
    app = papp;
  }

  void setup(float c) {
    counter = app.random(1000);
    this.center = c;
    pos = new PVector(center, center, center);
    velocity = app.random(0.005 f.0.01 f);
    step = app.random(1000.f);
    ease = 0.06 f;
    organize = false;
  }

  void update(a) {
    
    float width = app.width;
    float height = app.height;
    
    if (organize) {
      pos.x += ((app.round(pos.x / width * 10) * width / 10) - pos.x) * velocity;
      pos.y += ((app.round(pos.y / height * 10) * height / 10) - pos.y) * ease;
      pos.z += ((app.round(pos.z / height * 10) * height / 10) - pos.z) * ease;
    } else {
      counter += velocity;
      pos.x += ((app.noise(counter * 0.5 f + step) - 0.25 f) * width * 2 - pos.x) * ease;
      pos.y += ((app.noise(counter * 0.3 f + step) - 0.25 f) * height * 2 - pos.y) * ease;
      pos.z += ((app.noise(counter * 0.1 f + step) - 0.25 f) * height * 2 - pos.z) * ease;
    }
    size = app.map(pos.z / width, 0.0 f.0.5 f.3f.10f);
  }

  abstract void draw(a); 
}
Copy the code

The concrete Dot class definition, in this case spherical, mainly implements the draw method not implemented in the base class

import processing.core.*;

public class SphereDot extends Dot {

  public float prevRotateAngle = 0.f;
  public float destRotateAngle = 0.f;
  public float rotateAngle = 0.f;
  public float changeTime = 60.f;
  public float lastChangeFrameCount = 0.f;

  SphereDot(PApplet papp) {
    super(papp);
  }

  void draw(a) {

    float frameCount = app.frameCount;
    boolean tempOrganize = frameCount % (changeTime * 2) > changeTime;
    if(organize ! = tempOrganize) { lastChangeFrameCount = frameCount; organize = tempOrganize; prevRotateAngle = rotateAngle; destRotateAngle += app.PI /2;
    }
    
    app.push();
    app.translate(center, center, center);
    float easeLerp = iease.ease((frameCount - lastChangeFrameCount) / changeTime);
    rotateAngle = app.lerp(prevRotateAngle, destRotateAngle, easeLerp);
    app.rotateY(rotateAngle);

    prevRotateAngle = rotateAngle;
    destRotateAngle += app.PI / 2; app.translate(-center, -center, -center); app.pop(); app.push(); app.translate(pos.x, pos.y, pos.z); app.sphere(size); app.pop(); }}Copy the code

As a result, our client code will be very clean

ArrayList<Dot> dots = new ArrayList<Dot>();

void setup(a) {
  
  size(512.512, P3D);
  float center = width/2;
  ortho(-center, center, -center, center, 0.1000);
  
  int totalDots = 100;
  for(int i=0; i<totalDots; i++){
    Dot dot = new SphereDot(this);

    /// @note toggle different slow classes
    //dot.iease = new EasingEaseInCubic(this);
    //dot.iease = new EasingEaseOutCubic(this);
    //dot.iease = new EasingEaseInOutCubic(this);
    dot.iease = new EasingEaseInOutQuint(this);
    dot.setup(center);
    dots.add(dot);
  }
  
  noStroke();
  //fill(255);
  frameRate(30);
} 

void draw(a) {
  background(0);  
  
  for(int i=0; i<dots.size(); i++){ dots.get(i).update(); dots.get(i).draw(); }}Copy the code

It is also easy to modify the effect of square particles via bridge mode, inheriting directly from the Dot class to get the BoxDot class. You can then choose the appropriate easing function class according to your preference, without affecting the previously defined classes.

rendering

Slow effects of spherical particles

  1. EasingEaseInCubic

  1. EasingEaseOutCubic

  1. EasingEaseInOutCubic

  1. EasingEaseInOutQuint (change the easing function to square particles)

Advantages and disadvantages of the bridge mode

Advantages:

  • Separate the abstract and implementation parts. The bridge pattern decouples the inherent binding between abstractions and implementations using “associations between objects,” allowing abstraction and implementation to vary along their respective dimensions. That is, the abstraction and implementation are no longer in the same inheritance hierarchy, but are “subclassed” so that they each have their own subclasses so that they can be composed arbitrarily, resulting in multi-dimensional composite objects.

  • In many cases, the bridge pattern can replace the multi-layer inheritance scheme (Java is single-inheritance, C++ is multi-inheritance). Multi-level inheritance violates the principle of “single responsibility” and has poor reusability and a large number of classes. So the bridge mode is better, it greatly reduces the number of subclasses.

  • The expansibility of the system is improved. It does not need to modify the original system to expand any one of the two change dimensions, which conforms to the “open and closed principle”.

Disadvantages:

  • It is more difficult to understand and design the system. Since the relationship is established in the abstraction layer, developers are required to design and program for the abstraction layer from the very beginning.

  • It is necessary to correctly identify the two independent changing dimensions in the system, so the range of use has certain limitations, and how to correctly identify the two independent dimensions also requires certain experience accumulation.