OOP & Connascence

Codesai

Fran Reyes / @fran_reyes

Alfredo Casado / @alfredocasado

Structured Design

book from 1975

cost total = cost develop + cost maintain

cost maintain = cost understand + cost change +

           cost test + cost deploy

Software Economics

Coupling

Degree of interdependence between software modules.

Kent Beck

“Coupling between A and B is defined as "if I change element A I also have to change element B"”

Cohesion

Degree to which the elements of a module belong together.

Kent Beck

“Cohesion of an element A is defined as "if I change subelement A.1, do I have to change all the other subelements A.2-n?". So, cohesion is coupling within an element”

Nothing new

book from 1995

More resources

  • Talk "Gran unified theory of sofware development" by Jim  Weirich (https://vimeo.com/10837903)
  • Kevin Rutherford Articles (https://silkandspinach.net/tag/connascence/)
  • Connascence.io

Connascence

Connascence between 2 software components A and B means either

That you can postulate some change to A that would require B to be changed (or at least carefully checked) in order to preserve overall correcteness, or

That you can postulate some change that would require both A and B to be changed together in order to preserve overall correcteness.

A more simple definition

 

Connasnence is a vocabulary to talk about coupling

 

Connascence Types

 

Name, Type, Meaning, Algorithm, Position

Execution, Timing, Value, Identity

 

Three separate axes 

  • Degree
  • Locality
  • Strenght

Degree

Number of elements affected by the connascence

Locality

  • In the same function?

  • in the same class?

  • in the same package/namespace?

  • in the same bounded context?

  • in the same application?

Strength

  1. Name
  2. Type
  3. Meaning
  4. Algorithm
  5. Position
  6. Execution
  7. Timing
  8. Value
  9. Identity

Static

Dynamic

Connascence of name

public class Test {
    ...
    var ob1 = new Ob1();
    ob1.doSomething();
    ...
}

public class Ob1 {
    public void doSomething(){ //code }
}

Connascence of name

public class User {
    public string Name { get; set; }
}
...
var user = connection.Query<User>("select name from users where id=1");
...
Console.WriteLine("Username is: " + user.name);

Connascence of Type

when multiple components must agree on the type of an entity

public class Credentials {
   ...
}

public class LoginService {
    public bool login(Credentials credentials) {
      ...
    }
}

loginService.login(new Credentials(...));

Connascence of Type

In dynamically typed langauages can be more dangerous 

def calculate_age(birth_day, birth_month, birth_year):
    ...


// How call this method?

calculate_age(1, 9, 1984)
calculate_age(1, 9, 84)
calculate_age('1', '9', '1984')
calculate_age('1', 'September', '1984')

Connascence Meaning

when multiple components must agree on the meaning of particular values

// Java comparable

a.compareTo(b) 
// return 0 if are equal
// return positive number if a > b
// return negative number if b > a

Connascence Meaning

public User getUser(string userId) { ... }

User user = obj.getUser("id");
if (user != null) {
    // do something
}

// refactor to convert from CoMeaning to CoType
public Maybe<User> getUser(string userId) { ... }

Maybe<User> user = obj.getUser("id");
if (user.exits()) {
    // do something
}

Connascence of Algorithm

// when the server receives the book request
public class BookTaxi {
    public void Book(Date reservationDate,...) {
        if (!isInTheNextFiveDays(date)) {
            return throw BookException(...)
        }
    }
}

// in client code, js for example
function BookTaxi(date,...) {
    if (!isInTheNextFiveDays(date)) {
        // show a message to the user
    }
}

when multiple components must agree on a particular algorithm

Connascence of Algorithm

 
def write_data_to_cache(data_string):
    with open('/path/to/cache', 'wb') as cache_file:
        cache_file.write(data_string.encode('utf8'))

def read_data_from_cache():
    with open('/path/to/cache', 'rb') as cache_file:
        return cache_file.read().decode('utf8')


// using a object that encapsulates the encoding/decoding algorithm
def write_data_to_cache(data_string):
    with open('/path/to/cache', 'wb') as cache_file:
        cache_file.write(self.Algorithm.encode(data_string)

def read_data_from_cache(codificationAlgorithm):
    with open('/path/to/cache', 'rb') as cache_file:
        return self.Algorithm.decode(cache_file.read())

Connascence of Position

 
// connascence of position
function serverCall(url, method, data, okCallback, errorCallback) {
....
}

serverCall("/endpoint", "post", {foo: 'foo'}, ()=> {
    // do something
}, ()=> {
    // handle error
});

when multiple entities must agree on the order of values.

Connascence of Position


// CoP -> CoN
function serverCall({url, method, data, ok, error})

serverCall({
    url: '/endpoint',
    method: 'post',
    data: {foo: 'foo'},
    ok: ()=>{// do somehting},
    error: ()=>{// handle error}
});

Connascence of Position

 
def calculateImageSize(url):
    width = ...
    height = ...
    return (width, height)

(width, height) = calculateImageSize('/url/img.png')
print(width)

// using named tuples reduce from CoPosition to CoName
ImageSize = namedtuple('ImageSize', 'width height')

def calculateImageSize(url):
    width = ...
    height = ...
    return ImageSize(width, height)

imageSize = calculateImageSize('/url/img.png')
print(imageSize.width)

Connascence of Execution

 

 
Configurator configurator = new Configurator();
configurator.setModel("Ferrari F40");
List<Equipments> availableEquipments = configurator.getEquipmentsResultSet();

// refactor to reduce from CoExecution to CoType
Configurator configurator = new Configurator();
Model model = configurator.getModel("Ferrari F40");
List<Equipments> availableEquipments = model.getEquipmentsResultSet();

when the order of execution of multiple components is important.

Connascence of Execution

 

 

The general workflow is something like this

  • Instanciate a Configurator object
  • Set the required attributes by calling the set methods e.g. setModel(String)
  • Call one of the get methods e.g. getEquipmentResultSet().
  • Iterate over the returned result ...

Coupled with documentation!

from javadoc:

Connascence of Timing

 

 

// using bootstrap modal
$(element).modal('hide')
$(element).modal('show') // Error!

// hiding a modal has an animation of aprox 500ms, if you call 'show' during 
// this animation the component breaks in unexpected ways

// we need to do something like this
$(element).modal('hide')
$(element).on('hidden.bs.modal', ()=>{
    $(element).modal('show') // ok
})

when the timing of the execution of multiple components is important

https://github.com/twbs/bootstrap/issues/3902

Connascence of Value

 

when several values must change together.

[Test]
public void total_price_for_scaned_products_with_discount() {

  var productScanner = new ProductScanner();
  productScanner.Scan("productA");
  productScanner.Scan("productB");
  
  productScanner.Total.Should().Be(150);
}

class ProductScanner {
  private readonly HashMap<string, int> product_prices = new HashMap<string, int> {
    {"productA", 100},
    {"productB", 50}
  }

  public int Total {get; private set;} 
  
  public void Scan(String productName) {
    Total += product_prices[productName];
  } 
}

Production code and test agree about the values of the products

private readonly HashMap<string, int> product_prices = new HashMap<string, int> {
  {"productA", 100},{"productB", 50}
}

[Test]
public void total_price_for_scaned_products_with_discount() {
  var productScanner = new ProductScanner(product_prices);
  productScanner.Scan("productA");
  productScanner.Scan("productB");
  
  productScanner.Total.Should().Be(150);
}

class ProductScanner {

  private readonly HashMap<string, int> product_prices;

  public ProductScanner(HashMap<string,int> product_prices) {
    this.product_prices = product_prices;
  }
  
  public int Total {get; private set;} 
  
  public void Scan(String productName) {
    Total += product_prices[productName];
  } 
}

Reduce locality

productScanner.Total.Should()
              .Be(product_prices["productA"] + product_prices["productB"]);

We can eliminate CoV with this change:

productScanner.Total.Should().Be(150);

instead of:

Now we have CoAlgorithm, less connascence strength

but... is really a best solution?

Connascence of Identity

when multiple components must reference the same entity

Connascence of Identity

const eventBus = EventBus();

const mainComponent = MainComponent(eventBus);
const component1 = Component1(eventBus)
const component2 = Component2(eventBus)
const component3 = Component3(eventBus)

Connascence

By Fran Reyes Perdomo