Fundamentals of Programming

Some of my personal notes and references regarding general programming.

 

SOLID

A collection of principles that outline how software engineers can design and develop code that is easy to read, has flexibility to evolve and therefore easier to maintain. It was first defined by Robert C Martin (Uncle Bob) in his book – Design Principles and Design Patterns. It is considered a standard for many programmers.

 

Single Responsibility Principle

We want methods and classes to focus on specific functions. We dont want it to do too many things. This causes inner-dependencies that can increase complexity. In other words, we want to high decoupling and low cohesion. An example of this is when we want to decouple the presentation layer with the business layer.

A concern to this approach is that it can cause a large set of classes being created. But each class is very specific functionality.

using System;

namespace s
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Demonstration of SRP - Single Responsibiliy Principle");
            Example1();
            Example2();
            
            // Output
            // Jane Smith (Doe), Jane.Smith@email.com, 1
            // Jane Smith (Doe), Jane.Smith@email.com, 1
        }

        // Example 1, no classes, everything in single place
        static void Example1()
        {
            // Declarations
            var firstname = string.Empty;
            var lastname = string.Empty;
            var maidenname = string.Empty;
            var email = string.Empty;
            var eid = 0;

            // Inputs
            firstname = "Jane";
            lastname = "Doe";
            email = "Jane.Doe@email.com";

            // Actions
            eid = 1;
            email = "Jane.Smith@email.com";
            maidenname = "Doe";
            lastname = "Smith";
            Console.WriteLine($"{firstname} {lastname} ({maidenname}), {email}, {eid}");
        }

        // Example 2, break everything out into its own classes
        static void Example2()
        {
            Employee e = new Employee("Jane", "Doe", 1);
            e.ChangeLastName("Smith");
            Console.WriteLine($"{e.FirstName} {e.LastName} ({e.MaidenName}), {e.Email}, {e.Eid}");
        }
    }

    public interface Person
    {
        string FirstName {get; set;}
        string LastName {get;set;}
    }

    public class Employee : Person
    {
        public string FirstName {get;set;}
        public string LastName {get;set;}
        public string MaidenName {get;set;}
        public int Eid {get;set;}
        public string Email
        {
            get { return this.EmailObj.Address; }
        }
        private Email EmailObj;

        public Employee(string fname, string lname, int eid) 
        {
            this.FirstName = fname;
            this.LastName = lname;
            this.Eid = eid;
            this.EmailObj = new Email(GetEmail());
        }

        public void ChangeLastName(string newlastname)
        {
            this.MaidenName = this.LastName;
            this.LastName = newlastname;
            this.EmailObj = new Email(GetEmail());
        }

        private string GetEmail()
        {
            return $"{this.FirstName}.{this.LastName}@email.com";
        }
    }

    public class Email
    {
        public string Address {get;set;}

        public Email(string email) 
        {
            if (ValidateEmail(email)) this.Address = email;
        }

        public void ChangeEmail(string newEmail)
        {
            if (ValidateEmail(newEmail)) this.Address = newEmail;
        }

        private bool ValidateEmail(string email)
        {
            return email.Contains("@");
        }
    }
}

 

Open Closed Principle

Keep classes open for extension but closed for modifications. We dont want the core functionality of a class to change, however, certain properties of a class should be easily extendable. This helps the code stay robust when there are future changes.

For example below we have two different ways to define an employee. The first approach has a generic Worker class. Later if we have changes such that we need to track different types of workers (Employee vs Contractor), of which each type has different properties, the Worker class requires major refactoring. However in the second example, we’re able to abstract some of the common properties out into a Person Interface, thereby making it easier to accommodate future changes.

using System;

namespace o
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Open Closed Principle");
        }
    }

    // Example1 - Worker class is unable to adjust to future changes
    public class Worker
    {
        string FirstName { get; set; }
        string LastName { get; set; }
        string EmailAddress { get; set; }
        int EmployeeId { get; set; } // only for employee
        string OfficeLocation { get; set; } // only for employees
        int ContractorId { get; set; } // only for contractors
        string Company { get; set; } // only for contractors

        string MaidenName { get; set; } // Was added later
    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
        string MaidenName { get; set; }
        string EmailAddress { get; set; }

        public void ChangeLastName(string newlastname);
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MaidenName { get; set; }
        public string EmailAddress { get; set; }
        public int EmployeeId { get; set; }
        public string OfficeLocation { get; set; }

        public void ChangeLastName(string newlastname)
        {
            this.MaidenName = this.LastName;
            this.LastName = newlastname;
        }
    }

    public class Contractor : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MaidenName { get; set; }
        public string EmailAddress { get; set; }
        public int ContractorId { get; set; }
        public string Company { get; set; }

        public void ChangeLastName(string newlastname)
        {
            this.MaidenName = this.LastName;
            this.LastName = newlastname;
        }
    }
}

 

Liskov Substitution Principle

Child classes (derived classes) should be able to replace parent methods or properties without breaking the application. The child can extend the parent functions but cannot modify.

For example, create an IRepository that classes can interface with. Behind that IRepository is the different implementations but that should not be shown to the classes referencing it. The interface has the core functionality exposed.

using System;

namespace l
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Liskov Subsitution Principle");
        }
    }

    public interface IEmployee
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public abstract class BaseEmployee : IEmployee
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Grade { get; set; }

        public virtual int CalculateRate()
        {
            int baserate = 12;
            return baserate * this.Grade;
        }
    }

    public class Contractor : BaseEmployee
    {
        public override int CalculateRate()
        {
            int baserate = 14;
            return baserate * this.Grade;
        }
    }

    public class Manager : BaseEmployee
    {
        public override int CalculateRate()
        {
            return base.CalculateRate();
        }
    }
}

 

Interface Segregation Principle

Make fine grained interfaces that is specific to the function. This way no client or calling class needs to have access to unnecessary/unused functions in an interface. We should reduce code objects down to the smallest required implementation thus creating interfaces with only required declarations. As a result, an interface that has a lot of different declarations should be split up into smaller interfaces

using System;

namespace i
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Interface Segregation Principle");
        }
    }

    interface IPerson
    {
        string Name { get; set; }
        string SSN { get; set; }
    }

    interface IEmployee : IPerson
    {
        int EmployeeId { get; set; }
        string OfficeAddress { get; set; }
    }

    class Manager : IEmployee
    {
        public string Name { get; set; }
        public string SSN { get; set; }
        public int EmployeeId { get; set; }
        public string OfficeAddress { get; set; }
    }
}

 

Dependency Inversion

Use abstractions and not concrete implementations. The concrete implementations get injected at run-time. Clients interact via these abstractions. This allows for flexibility in how the concrete implementations are conducted.

  • High-level modules should not depend on low-level modules.  Both should depend on abstractions.
  • Abstractions should not depend on details.  Details should depend on abstractions.
using System;
using System.Collections.Generic;

namespace d
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Dependency Inversion Principle");

            SMS messenger = new SMS();
            //Text messenger = new Text();
            MessageSender sender = new MessageSender(messenger); // Inject type here
            sender.Send("Hello World!");
        }
    }

    interface IMessage
    {
        void SendMessage(string message);
    }

    class SMS : IMessage
    {
        public void SendMessage(string message)
        {
            // Send message via SMS
        }
    }

    class Text : IMessage
    {
        public void SendMessage(string message)
        {
            // Send message via Text
        }
    }

    class MessageSender
    {
        IMessage _messager;

        public MessageSender(IMessage messager)
        {
            this._messager = messager;
        }

        public void Send(string message)
        {
            this._messager.SendMessage(message);
        }
    }
}

 


Common Design Patterns

 

Chain of Responsibility Pattern

The CoR pattern is to create handlers that can do some logic. That logic will have some result which then can be used in the chain for the code to flow. Basically we will have a chain of Handlers.

using System;

namespace DnetChainResp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Chain of Responsiblity Pattern");

            User u1 = new User
            {
                Age = 21,
                Name = "John Doe",
                SSN = "123456789"
            };

            User u2 = new User
            {
                Age = 18,
                Name = "Jane Smith",
                SSN = "12345"
            };

            var handler = new SSNHandler();
            handler.SetNext(new AgeHandler());

            handler.Handle(u1); // Passes
            handler.Handle(u2); // Fails
        }
    }

    public interface IHandler where T : class
    {
        public IHandler SetNext(IHandler next);
        public void Handle(T request);
    }

    public abstract class Handler : IHandler where T : class
    {
        private IHandler Next { get; set; }

        public virtual void Handle(T request)
        {
            Next?.Handle(request);
        }

        public IHandler SetNext(IHandler next)
        {
            Next = next;
            return Next;
        }
    }

    public class User
    {
        public string Name { get; set; }
        public string SSN { get; set; }
        public int Age { get; set; }
    }

    public class SSNHandler : Handler
    {
        public override void Handle(User request)
        {
            if (request.SSN.Length != 9)
            {
                throw new Exception("Failed SSN Handler");
            }
            base.Handle(request);
        }
    }

    public class AgeHandler : Handler
    {
        public override void Handle(User request)
        {
            if (request.Age <= 18)
            {
                throw new Exception("Failed Aged Handler");
            }
            base.Handle(request);
        }
    }
}

 

State Design Pattern

One of the 23 design patterns defined by the Gang of Four. The state pattern is a behavioral software design pattern that allows an object to alter its behavior when its internal state changes. This pattern is close to the concept of finite-state machines. The state pattern can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern’s interface. State is a condition of some variable.

Naive patterns may use variables to track state. Then we would need one or many conditional statements to check that state. This may work for simple cases but when there are complex states this pattern will fail.

State Design Pattern has the following elements:

  • Context – is the current state
  • Abstract State – an interface for all state specific behaviors
  • Concrete State – the different context states available

The pattern is implemented by doing the following:

  • List all possible states
  • List conditions for transitions between those states
  • Define the initial state

An example of this can be seen here:

Corresponding Code:

using System;

namespace DnetState
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("State Design Pattern");

            BookingContext e1 = new BookingContext();
            e1.SubmitDetails("John Smith", 1);
            e1.TransitionToState(new BookedState());
            e1.DatePassed();
            //STATE: New
            //STATE: Pending
            //STATE: Booked
            //Closing - Existing Booking Completed
            //STATE: Closed

            BookingContext e2 = new BookingContext();
            e2.SubmitDetails("John Doe", 3);
            e2.TransitionToState(new BookedState());
            e2.Cancel();
            //STATE: New
            //STATE: Pending
            //STATE: Booked
            //Closing - Existing Booking Canceled
            //STATE: Closed
        }

    }

    public class BookingContext
    {
        public string Attendee { get; set; }
        public int TicketCount { get; set; }
        public int BookingId { get; set; }
        private BookingState currentState { get; set; }

        public BookingContext()
        {
            TransitionToState(new NewState());
        }
        public void SubmitDetails(string attendee, int ticketCount)
        {
            currentState.EnterDetails(this, attendee, ticketCount);
        }
        public void Cancel()
        {
            currentState.Cancel(this);
        }
        public void DatePassed()
        {
            currentState.DatePassed(this);
        }
        public void TransitionToState(BookingState state)
        {
            currentState = state;
            currentState.EnterState(this);
        }
        public void ShowState(string state)
        {
            Console.WriteLine($"STATE: {state}");
        }
    }

    public abstract class BookingState
    {
        public abstract void EnterState(BookingContext booking);
        public abstract void Cancel(BookingContext booking);
        public abstract void DatePassed(BookingContext booking);
        public abstract void EnterDetails(BookingContext booking, string attendee, int ticketCount);
    }

    public class NewState : BookingState
    {
        public override void Cancel(BookingContext booking)
        {
            booking.TransitionToState(new ClosedState("New Booking Canceled"));
        }
        public override void DatePassed(BookingContext booking)
        {
            booking.TransitionToState(new ClosedState("New Booking Expired"));
        }
        public override void EnterDetails(BookingContext booking, string attendee, int ticketCount)
        {
            booking.Attendee = attendee;
            booking.TicketCount = ticketCount;
            booking.TransitionToState(new PendingState()); // Change state
        }
        public override void EnterState(BookingContext booking)
        {
            booking.BookingId = new Random().Next();
            booking.ShowState("New");
        }
    }

    public class ClosedState : BookingState
    {
        private string reason;
        public ClosedState(string reason)
        {
            this.reason = reason;
        }
        public override void Cancel(BookingContext booking)
        {
            throw new NotImplementedException();
        }
        public override void DatePassed(BookingContext booking)
        {
            throw new NotImplementedException();
        }
        public override void EnterDetails(BookingContext booking, string attendee, int ticketCount)
        {
            throw new NotImplementedException();
        }
        public override void EnterState(BookingContext booking)
        {
            Console.WriteLine($"Closing - {reason}");
            booking.ShowState("Closed");
        }
    }

    public class PendingState : BookingState
    {
        public override void Cancel(BookingContext booking)
        {
            throw new NotImplementedException();
        }
        public override void DatePassed(BookingContext booking)
        {
            throw new NotImplementedException();
        }
        public override void EnterDetails(BookingContext booking, string attendee, int ticketCount)
        {
            throw new NotImplementedException();
        }
        public override void EnterState(BookingContext booking)
        {
            booking.ShowState("Pending");
        }
    }

    public class BookedState : BookingState
    {
        public override void Cancel(BookingContext booking)
        {
            booking.TransitionToState(new ClosedState("Existing Booking Canceled"));
        }
        public override void DatePassed(BookingContext booking)
        {
            booking.TransitionToState(new ClosedState("Existing Booking Completed"));
        }
        public override void EnterDetails(BookingContext booking, string attendee, int ticketCount)
        {
            throw new NotImplementedException();
        }
        public override void EnterState(BookingContext booking)
        {
            booking.ShowState("Booked");
        }
    }
}

 

 

Composite Pattern

In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes a group of objects that is treated the same way as a single instance of the same type of object. The intent of a composite is to “compose” objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.

In the example below, each node of the tree is a “Component”. This class can be treated uniformly by clients. Depending on the type of node, we have “Composite” and “Leaf” classes that implement the “Component”. These classes have behaviors specific to them, as well as it’s parent’s properties and methods.

using System;
using System.Collections.Generic;

namespace DnetComposite
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Composite Design Pattern");

            var root = new Composite("root");
            root.Add(new Leaf("LA"));
            root.Add(new Leaf("LB"));
            var comp1 = new Composite("C1");
            comp1.Add(new Leaf("C1.LA"));
            comp1.Add(new Leaf("C1.LB"));
            var comp2 = new Composite("C2");
            comp2.Add(new Leaf("C2.LA"));
            comp2.Add(new Leaf("C2.LB"));
            comp1.Add(comp2);
            root.Add(comp1);
            root.Add(new Leaf("LC"));

            var leaf = new Leaf("LD");
            root.Add(leaf);
            root.Remove(leaf);

            root.PrimaryOperation(1); // Example1
            Console.WriteLine("------------------");
            comp1.PrimaryOperation(1); // Example2
            Console.WriteLine("------------------");
            comp2.PrimaryOperation(1); // Example3

            //-root
            //-- - LA
            //-- - LB
            //-- - C1
            //---- - C1.LA
            //---- - C1.LB
            //---- - C2
            //------ - C2.LA
            //------ - C2.LB
            //-- - LC
            //------------------
            //- C1
            //-- - C1.LA
            //-- - C1.LB
            //-- - C2
            //---- - C2.LA
            //---- - C2.LB
            //------------------
            //- C2
            //-- - C2.LA
            //-- - C2.LB
        }
    }

    public abstract class Component
    {
        public string Name { get; }
        public Component(string name)
        {
            this.Name = name;
        }
        public abstract void PrimaryOperation(int depth);
    }

    public class Leaf : Component
    {
        public Leaf(string name) : base(name) { }

        public override void PrimaryOperation(int depth)
        {
            Console.WriteLine(new String('-', depth) + this.Name);
        }
    }

    public class Composite : Component
    {
        private List children = new List();
        public Composite(string name) : base(name) { }

        public override void PrimaryOperation(int depth)
        {
            Console.WriteLine(new String('-', depth) + this.Name);
            foreach (var component in this.children)
            {
                component.PrimaryOperation(depth + 2);
            }
        }
        public void Add(Component c)
        {
            this.children.Add(c);
        }
        public void Remove(Component c)
        {
            this.children.Remove(c);
        }
    }
}

 

 


Core programming concepts covered below.

 

Mutable and Immutable Objects

Mutable basically means the object can be modified after creation whereas Immutable objects cannot be modified after creation. This is easily seen in the example below.

names = ['adam','ben','charlie','denise']
output = ',';
for name in names:
 output += name + output; // adam, - each iteration is stored in a new memory block
print output // adam, ben, charlie, denise

 

Its important to note that in most programming languages (C#/Java), the primitive string type is immutable. To make it mutable, we use objects such as StringBuffer. When we try to perform actions like concatenation on a string object directly, behind the scenes there a multiple copies of that object being created in new memory addresses. To avoid this inefficiency, we would want to use a StringBuffer when trying to modify a string.

 

Serialization

In computer science, in the context of data storage, serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment). When the resulting series of bits is reread according to the serialization format, it can be used to create a semantically identical clone of the original object. For many complex objects, such as those that make extensive use of references, this process is not straightforward. Serialization of object-oriented objects does not include any of their associated methods with which they were previously linked.

 

Collections

In computer science, a collection or container is a grouping of some variable number of data items (possibly zero) that have some shared significance to the problem being solved and need to be operated upon together in some controlled fashion. Generally, the data items will be of the same type or, in languages supporting inheritance, derived from some common ancestor type. A collection is a concept applicable to abstract data types, and does not prescribe a specific implementation as a concrete data structure, though often there is a conventional choice (see Container for type theory discussion).

Examples of collections include lists, sets, multisets, trees and graphs.

 

First-Class Functions

A first-class function is a function that is treated like a variable. As such, the function can be passed as an argument to other functions and can be returned by another function and can be assigned as a value to a variable.

function square(x) {
 return x * x;
}
function myMap(func, args) {
 result = [];
 for (var i = 0; i < args.length; i++) {
  result.push(func(i));
 }
 return result;
}
var f = square; // f is a first class function
var b = myMap(f, [1,2,3,4,5]); // b is a first class function using another first class function

console.log(f(5)); // displays 25
console.log(b); // [1, 8, 27, 64, 125]

 

Closures

A closure is an inner function that has access to the outer (enclosing or parent) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

In JavaScript, closures go hand-in-hand with first-class functions. We can create a first-class function as a closure and then it is possible to call that closure. In the example below, the counter variable is local to the add first-class function and the inner function within it which does the increment. This is how we could implement a counter without exposing the counter variable to anyone outside that first-class function.

var add = (function () {
    var counter = 0;
    return function () {counter += 1; return counter}
})();

add();
add();
add();  // the counter is now 3

Another example is shown below using C#. The thing to remember is that a closure allows variables that are outside the scope of the function to be referenced. But only when using first-class functions. The inner function below is able to access the ‘myVar’ variable in it’s parent’s scope.

static void Main(string[] args)
{
 var inc = GetAFunc();
 Console.WriteLine(inc(5));
 Console.WriteLine(inc(6));
}

public static Func<int,int> GetAFunc()
{
 var myVar = 1;
 Func<int, int> inc = delegate(int var1)
 {
 myVar = myVar + 1;
 return var1 + myVar;
 };
 return inc;
}

 

Memoization

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. Memoization has also been used in other contexts (and for purposes other than speed gains), such as in simple mutually recursive descent parsing. It is similar to caching results of calculations or variables.

cache = []

function myfunc(num) {
 if (num in cache) {
  return cache[num];
 }
 print 'Computing ' + num + '... ';
 time.sleep(1);
 result = num * num;
 cache[num] = result;
 return result;
}
result = myfunc(4); print result;
result = myfunc(10); print result;
result = myfunc(4); print result;
result = myfunc(10); print result;

// Output:
Computing 4...
16
Computing 10...
100
16
100

 

Combinations and Permutations

A combination is a way to group elements where order does not matter. A permutation is a way to group elements where order does matter.

array = [1,2,3]
combinations = intertools.combinations(array, 3) // look for ways where order does not matter
for c in combinations:
 print c

// Output:
(1,2,3) // order does not matter

// if code was instead - intertools.combinations(array, 2)
(1,2)
(2,3)
(1,3)

permations = itertools.permutations(array, 2)
for p in permutations:
 print 2

// Output
(1,2)
(1,3)
(2,1)
(2,3)
(3,1)
(3,2)

 

Idempotence

Pronounced item-pot-ence. This is a property of an operation where when applied multiple times it does not change the results from the initial application. In it’s simpilist form it can be seen as follows:

f(f(x)) = f(x)

A more in depth example:

deff addten(num): 
 return num + 10

print addten(10) // 10
print addten(addten(10)) // 30 - this is not idempotent

print abs(10)  // 10
print abs(abs(10)) // 10 - this is idempotent

As the examples above shows, idempotent basically means you get the same result even when calling multiple times

Idempotent is seen in REST APIs.

GET - idempotent
PUT - idempotent, inital record is still there and returned (though some fields may be updated)
POST - not idempotent, changes data
DELETE - idempotent, when trying to redelete a record, it cant find it so the state of the database is still the same and therefore idempotent

 

String Interpolation

Process of evaluating a string literal using placeholders that results in new values based on those placeholders.

name = 'john'
print 'Hello world' + name; // This string concatenation, not interpolation'
print `Hello world ${name}`; // This is string interpolation

 

Types of Languages

Languages from low to high:

  • Machine Code – 1010010
  • Assembly – ADD R1, R2;
  • System / High Level Language – (works closely with the system, embedded programming ) C / C++
  • Application / High Level Language – C# / Java

Compiled Language = high level language that is compiled down to machine code (C / C++)

Interpreted Language = an interpreter interprets the high level language and able to run it through the machine. Does not need compilation. (Perl / BASH)

 


Functional Programming

A programming paradigm, like object-oriented programming. It uses functions as the flow of the program. Everything is a function with inputs and outputs.

function hello(name) {
  return "Hello " + name;
}
hello("world!");

Functional Programs should avoid outside dependencies. The hello function above is considered pure because it strictly takes an input which causes the output. There is also the concept of higher order function, in which a function is returning or has nested other functions.

function higher(name) {
  return function(string) {
    return name + ',' + string;
  };
}
var h = higher('Hello');
h('World'); // Hello,World

Another concept of functional programming is to avoid iterations. This can be seen when using functions like map, reduce and filter.

Functional Programming also avoids data mutation (aka is immutable). The problem with immutability is that it causes new instances or copies, which can make this inefficient. To get around, Functional Programming uses persistent data structures. This is done with a concept called Structural Sharing.

In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified. Such data structures are effectively immutable, as their operations do not (visibly) update the structure in-place, but instead always yield a new updated structure.

https://hackernoon.com/how-immutable-data-structures-e-g-immutable-js-are-optimized-using-structural-sharing-e4424a866d56

 

 

 

 

 


References

Design Patterns – Erich Gamma, Richard Helm, Ralph Johnson, John Vlissades
https://en.wikipedia.org/wiki/Design_Patterns