Beigesoft™. Programming approaches.

Key words: Design principles, OOP patterns.

This is about the main point of "how to design a program in an optimal way". All these approaches are widely used in real life. So this is also about "get back from newest terms(words) to old ones".

1. Part - assembly approach. A part should be as atomic as possible. An assembly can consist of other assemblies (parts).

Real life example: a gear, an engine, an atom, a quark, etc.

OOP principles and patterns that follow to this approach:

OOP means "encapsulation", so in the old days programmers often filled "Objects" as much as possible, that is why those OOP design patterns came about.

You can look at https://github.com/demidenko05/beige-uml source that is a very good example of "part-assemble" approach. There an UML element is a full assemble (OOP) that includes all methods - invoking form (editor), draw, move, resize, etc. And this final assembly is made by final concrete platform-dependent factory, i.e. SWING and AWT or Android. Cross-platform logic parts include: element form editor, resize-movement service, etc.

Common violations of "part-assembly" approach.

1. OOP itself requires that an object must encapsulate methods. In C we can create object String{char *val; int len;}, then, WHEN IT'S NEEDED, we can create StringObject{String *val; int (*printer)(char*);}. As a result, programmers often prefer to use functional style in OOP language, because it's more easy and inexpensive. Remarkable example is Hibernate in Java.

2. Runtime annotations in Java. Assume you want to add JAXB for an object that is already an annotated JPA entity. First, you violate OCP and have to recompile this class. At second, mixing JAXB and JPA annotations definitely looks no good and might not work. You can look at Beige-EIS code, where for example DebtorCreditor entity has 4 different XML configurations: 1 - ORM, 2 - WEB FORM/LIST generator, 3 - full database copy via XML WEB-service, 4 - export from tax to financial database via XML WEB-service. So a configuration of how to use a class in a library (ORM, ...) is exactly another part that must be placed in its own place (file).

2. Interface or Abstraction of a part. This allows to make an assembly from parts by attaching via an interface (abstraction). So you can fix or change an assembly by substituting a part.

Real life example: an engine is connected to a transmission via a generic junction (interface), so you can choose between AT/MT4/MT6.

Functional programming - headers in C (public methods and data types). An interface can use super-abstractions such as void* pointers.

OOP. In Java there is exactly an "interface" type of file. You should use Abstract classes only to inherit properties and methods for extending classes, and abstract classes usually should implement interface/s.

Example of high level abstraction interface between Shapes and Pane with methods encapsulation in C: Shape{int startX; int startY;} and ShapeObj{Shape *shape, void (*draw)(Shape*);}. In Java this is: IShape {int getStartX(); int getStartY(); void setStartX(int x); void setStartY(int y); void draw();}

OOP principles and patterns that follow to this approach:

In Java, an interface should be used only when it's needed by a client. For example in Beige EIS DebtorCreditor has generic interfaces - IHasID, IHasVersion, IHasName, but methods "get/set TIN" are only this class's methods, because there is no a client that expects generic interface with this methods.

3. An interface of a part must follow the client (assembly) expectations, i.e. interface must be implemented in straightforward (non-ambiguous) meaning, especially in case of substitutions of parts.

Real life examples:

Programming - interface must be implemented in a straightforward way, without confusing a client (assembly). In case when a part is substituted by another one that has another meaning interface implementation, then already worked assembly might not work properly.

OOP principles and patterns that follow to this approach:

4. Triplet: the simple the better; use the best alternative; the more tests the better.

The main rule about part choosing: If a part is satisfied to all of the requirements, and it seems to be simpler than other alternatives, then this is the reason to use it, and there is no reasons to substitute it, except experimental or innovation purposes.

When you save time by using already working parts in a new assembly, then you have more time for testing.

Real life example: the same bearing has been used for many years in many countries (car manufacturers) in car engines, in wheel hubs, etc. Internal combustion engine is the best alternative yet, but actually the best alternative is hydrogen powered fuel cells. But because of the very vast legacy of gasoline infrastructure, substituting hydrogen for gasoline seems to be a very difficult and expensive task. But it resolves the greenhouse gas (CO2) problem, because this is actually the best energy accumulation technology that allows to fire much less coal, gas, etc. Why do they (organizations such as Greenpease, activists) never promote this? In contrast with substitution of hydrogen for gasoline, substituting HTML5+ and constrained plugins for JavaScript in WEB-clients (to make WEB-clients less vulnerable) seems to be a very easy task.

Software architects sometimes choose creating new own parts for the only reason - they must be totally ours. Maybe it's company policy - no advertising of free software.

Part of Java EE WEB-part: servlets, JSP... are the best alternative not only for WEB-interface. Even JSP is the best approach to make JSON responses (see in Beige-EIS payment gateway response RESTFUL). With servlets you can easily make anything - RESTFUL API, any arbitrary XML, CSV, PDF... In the old times Spring MVC plus Hibernate overcame other JEE technologies including EJB because of the best performance. But MVC was actually the JEE WEB part. Automatically filling a model from a request - isn't a big deal. Non-OOP Hibernate was turned into JPA. In Beige-EIS WEB-store buyers requests are processed by hand without any problem. Of course, it's used by Beige-WEB for making the catalog, prices, etc. And Beige-WEB isn't only for automatically filling models, it also allows to make reliable WEB-interface really quickly (almost automatically). Beige-WEB is based on its own ORM that is different from Hibernate approach, but Beige-ORM approach is faster than Hibernate one and it's cross-platform (it's also fastest on Android for practical complex tasks).

You might have heard about battlecode.org. They are chasing the highest performance in Java. They do not use even standard JSE collections, they use their own array-based collections. Is such approaches the best alternative for practical programming? Of course not, because at least they seem to be not simpler than JSE ones. Although JSE and JEE have hardly ever used parts. For example, JSP must be used only as a view or for making any other response (e.g. JSON), so tags like SQL are the dead weight.

5. Wrappers.

Wrappers reuse code and add functionality or/and adapt to new interface/s.

* This is not about JSE wrappers such as Integer that wraps the primitive int type.

For OOP this is the best "extending" approach. If you extend a non-abstract class in Java, then code-style or findbug will warn "extending a class several times reduces performance". This is also the only approach for adding/removing functionality during runtime.

OOP patterns that follow to this approach:

Example of wrapper in C that wraps generic GLIBC's file writer to write exactly boolean with error reporting:

void
  bsfwrite_bool (bool *pData, FILE *pFile)
{
  int cnt = 1;
  int wcr = fwrite(pData, sizeof (bool), cnt, pFile);
  if ( wcr != cnt )
  {
    if ( errno == 0 ) { errno = BSE_WRITE_FILE; }
    BSLOG_ERR
  }
}

6. Delegate.

Delegate is a method or an object with methods that passed as a parameter to handle a job.

Common types of delegates:

a) Iterable delegates. These are comparators, consumers, etc.

Examples in C:

#include <stdlib.h>
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
See https://github.com/demidenko05/bsdict/blob/master/dict/BsDicWord.h:
/**
 * <p>Type-unsafe consumer of just read dic.word.</p>
 * @param pRdWrd - just read d.word to consume
 * @param pInst - instrument to consume d.word
 * @return 0 to continue iteration, otherwise stop iteration
 * @set errno if error.
 **/
typedef int BsDicWord_Consume_Tus(BsDicWord *pRdWrd, void *pInst);

/**
 * <p>
 * Type-unsafe iterator trough whole dictionary, just read dic.word will be consumed
 * by given consumer, if consumer returned non-zero, then iteration will stop.
 * Client must invoke another type-safe wrapper of this iterator.
 * </p>
 * @param pDicFl - opened unwound dictionary file
 * @param pDeBufSz - client expected/predicted this max entry size in dictionary
 * @param pWd_Csm - dic.word consumer
 * @param pInst - instrument to consume d.word
 * @return BSR_OK_END or consumer's last return value, e.g. BSR_OK_ENOUGH or errno
 * @set errno if error.
 **/
typedef int BsDicWord_Iter_Tus(FILE *pDicFl, int pDeBufSz, BsDicWord_Consume_Tus *pWd_Csm, void *pInst);

Examples in Java:

java.util.List.sort(Comparator<? super E> c);

b) Event handle delegates. These are handlers, observers, listeners, callbackers (these are synonyms).

Examples in Java: see Dialog (Confirmation) in https://github.com/demidenko05/beige-uml/blob/master/beige-swing-lib/src/main/java/org/beigesoft/service/swing/SrvDialog.java:

package org.beigesoft.service.swing;

import java.awt.Frame;
import javax.swing.JOptionPane;
import org.beigesoft.handler.IConsumer;
import org.beigesoft.handler.IHandlerConfirm;
import org.beigesoft.ui.service.ISrvDialog;

public class SrvDialog implements ISrvDialog<Frame> {

  @Override
  public void confirm(Frame frame, String msg, String title, IHandlerConfirm handlerConfirm) {
    int rez = JOptionPane.showConfirmDialog(frame, msg, title, 
        JOptionPane.YES_NO_OPTION, 
        JOptionPane.QUESTION_MESSAGE);
    if(rez == JOptionPane.YES_OPTION) {
      handlerConfirm.handleConfirm(true);
    }
    else if(rez == JOptionPane.NO_OPTION) {
      handlerConfirm.handleConfirm(false);
    }
  }

  @Override
  public void showAndGetString(Frame frame, String msg, String title, IConsumer<String> consumerString) {
     String result = JOptionPane.showInputDialog(frame, 
        msg, title);
     consumerString.consume(result);
  }
  ...
}
Here SWING (actually GTK) is not based on Handler Pattern, so JOptionPane.showConfirmDialog will freeze the program until the user clicks Yes or No. In opposite to this, Android uses exactly Handler Pattern, i.e. without waiting (freezing), see https://github.com/demidenko05/beige-uml/blob/master/beige-android-lib/src/main/java/org/beigesoft/android/ui/service/SrvDialog.java there android.app.AlertDialog uses exactly YES or NO Handlers:
package org.beigesoft.android.ui.service;

import org.beigesoft.android.R;
import org.beigesoft.android.ui.ListenerConfirmDialogNo;
import org.beigesoft.android.ui.ListenerConfirmDialogYes;
import org.beigesoft.android.ui.widget.DialogInputString;
import org.beigesoft.handler.IConsumer;
import org.beigesoft.handler.IHandlerConfirm;
import org.beigesoft.ui.service.ISrvDialog;
import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.app.AlertDialog;
import android.content.DialogInterface;

public class SrvDialog implements ISrvDialog<Activity> {

  @Override
  public void confirm(Activity activity, String msg, String title, IHandlerConfirm handlerConfirm) {
    AlertDialog.Builder builder = new Builder(activity);
    builder.setCancelable(false);
    builder.setMessage(msg).setTitle(title);
    builder.setPositiveButton(R.string.yes, new ListenerConfirmDialogYes(handlerConfirm));
    builder.setNegativeButton(R.string.no, new ListenerConfirmDialogNo(handlerConfirm));
    builder.show();
  }

  @Override
  public void showAndGetString(Activity activity, String msg, String title, IConsumer<String> consumerString) {
    DialogInputString dialogInputString = new DialogInputString(consumerString);
    dialogInputString.setTitle(title);
    dialogInputString.setMessage(msg);
    dialogInputString.show(activity.getFragmentManager(), DialogInputString.class.getSimpleName());
  }
  ...
}
Also the old JavaScript's modal conformation dialog was brutally disabled (maybe in 2016) in favor of delegates, e.g.:
  document.getElementById("buttonYes").onclick = yesHandler;

7. Creation patterns.

a) Concrete object creation

It's used directly in a part with using that object (client do not need an abstraction of an object), and it's used in factories (client needs an abstraction of an object)

a1) Constructor. This is the best alternative for most cases to create concrete objects. It's used in both functional and OOP styles. Setters are used for setting non-final object's properties. If creating an object seems to be complex (there are a lot of final properties), then Builder might be the best choice.

a2) Concrete objects Builders - is separated from an object part that holds all creation logic. This is an additional class (OOP) that holds all creation logic. In case of a complex object it saves resources (object in OOP doesn't have pointers to creation methods). Using builders everywhere seems to be exactly the Part-Assemble approach, but in simple cases it's excessive (more complex), in OOP a constructor must be.

b) Abstract object creation

Clients never know (and care) which concrete object should be created, e.g. an user wants to add a circle into the canvas, it choose menu->add circle, then client invokes the factory's method IShape createShape(String pType) where pType="circle".

b1) Factory's methods for creating/building abstract objects.

In C example see https://github.com/demidenko05/bsdict/blob/master/dict/BsDicObj.h creating and filling OOP-like object methods:

/**
 * <p>Generic, type-safe assembly of text/audio/both/... dictionary
 * with cached IDX head and methods (OOP like object).
 * This is interface for high level GUI.
 * This exposes abstractions (data and methods) that GUI needs.</p>
 * @member nme - file name plus state - e.g. indexing..., it will be hided in GUI with opened diIx-head->nme
 * @member pth - file path either from bsdict.conf or that user chose
 * @member opSt - opening shared data
 * @member pref - user preferences
 * @member diIx - text/audio/both/... dictionary with cached IDX head
 * @method diix_destroy - destroyer
 * @method diixfind_mtch - finder of matched words
 * @method diix_read - reader of content of found word
 **/
typedef struct {
  BsString *nme;
  BsString *pth;
  BsDiIxOst *opSt;
  BsDiPref *pref;
  BsDiIxBs *diIx;
  BsDiIx_Destroy *diix_destroy;
  BsDiIxFind_Mtch *diixfind_mtch;
  BsDiIx_Read *diix_read;
} BsDicObj;

/**
 * <p>Constructor.</p>
 * @param pPth - just chosen path
 * @param pIsIxRm - client prefers IRT (index records table) in memory (RAM) than in file
 * @return object or NULL when error
 * @set errno if error.
 **/
BsDicObj* bsdicobj_new (char *pPth, bool pIsIxRm);

/**
 * <p>Generic opener (load or create) of DIC IDX with methods object.</p>
 * @param pDiObj - dictionary object to open.
 * @clear errno if error with reporting
 **/
void bsdicobj_open (BsDicObj* pDiObj);

For Java see https://github.com/demidenko05/beige-uml/tree/master/beige-uml-android/src/main/java/org/beigesoft/uml/factory/android objects factories package which size is 252.1 KiB (i.e. 63% of 399.7 KiB total Java source code).

Beige-BLC uses a lot of abstract objects factories, because it's more flexible for extending/changing functionality. Abstraction always means "it's flexible". See https://github.com/demidenko05/beige-blc/tree/master/src/main/java/org/beigesoft/fct package where sub-factories produce abstract converters, processors, etc, and the main application factory produces all sub-factories and services by using Object's Name as a parameter and returns super-abstraction - java.lang.Object.

Factories is the more flexible, fast, powerful and cross-platform approach than IOC(CDI) containers. In case of never stopping enterprise applications, factories can implement any hot fixing logic.

c) Other methods (OOP). Factories without static methods seem to be the best alternative.

c1) Singleton (OOP). This is creating an only instance of a class by using the static way.

8. Lazy initializing (proxy).

This is about creating (allocating memory, etc.) desired object/s on demand. Otherwise unneeded objects waste your time and resources (memory). Factories and IOC(CDI) use (must) this approach.

ORM (Hibernate, etc) uses lazy initialization for retrieving desired object's tree in a flexible way. On one hand it's easy for changing business logic (simply change a JSP page). In other hand DB connection is opened during a whole request. It also takes a lot of SQL requests to retrieve desired data. Beige-ORM doesn't use this approach, it retrieves only needed data by using a single big SQL query, so it's faster.

References: