Html/Javascript widget

Tuesday, 26 July 2022

Strategy

The strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Strategy lets the algorithm vary independently. Deferring the decision about which algorithm to use until runtime allows the calling code to be more flexible and reusable.

For instance, a class that performs validation on incoming data may use the strategy pattern to select a validation algorithm depending on the type of data, its source, user choice or other factors. These factors are not known until run-time and may require radically different validation to be performed. The validation algorithms (strategies), encapsulated separately from the validating object, may be used by other validating objects in different areas of the system (or even different systems) without code duplication.


Typically, the strategy pattern stores a reference to some code in a data structure and retrieves it. This can be achieved by the native function pointer, the first-class function, classes or class instances in object-oriented programming languages or accessing the language implementation's internal storage of code via reflection.

Friday, 22 July 2022

Iterator

An iterator is an object that enables traversa of a container, mostly lists. Iterators are often provided via a container's interface. Though the interface and is fixed, iterators are often implemented in terms of the structures underlying a container implementation and are often tightly coupled to the container to enable the operational semantics of the iterator.

Internal iterators are higher order functions (often taking anonymous functions, but not necessarily) such as map(), reduce() etc., implementing the traversal across a container, applying the given function to every element in turn. An example might be Python's map function:

digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

squared_digits = map(lambda x: x**2, digits) # Iterating over this iterator would result in 0, 1, 4, 9, 16, ..., 81.

An external iterator may be thought of as a pointer with two primary operations: referencing one particular element in the object collection (called element access) and modifying itself so it points to the next element (called element traversal).There must also be a way to create an iterator so it points to some first element as well as some way to determine when the iterator has exhausted all of the elements in the container.

The primary purpose of an iterator is to allow a user to process every element of a container while isolating the user from the internal structure of the container. This allows the container to store elements in any manner it wishes while allowing the user to treat it as if it were a simple sequence or list. An iterator class is usually designed in tight coordination with the corresponding container class. Usually, the container provides the methods for creating iterators.

One way of implementing iterators is to use a restricted form of coroutine, known as a generator. By contrast with a subroutine, a generator coroutine can yield values to its caller multiple times, instead of returning just once. Most iterators are naturally expressible as generators, but because generators preserve their local state between invocations, they're particularly well-suited for complicated, stateful iterators, such as tree traversers. There are subtle differences and distinctions in the use of the terms "generator" and "iterator", which vary between authors and languages. In Python, a generator is an iterator constructor: a function that returns an iterator. An example of a Python generator returning an iterator for the Fibonacci numbers using Python's yield statement follows:

def fibonacci(limit):
a, b = 0, 1
for _ in range(limit):
yield a
a, b = b, a + b
for number in fibonacci(100): # The generator constructs an iterator
print(number)

Wednesday, 20 July 2022

Dependency Injection

In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. The pattern ensures that an object or function which wants to use a given service should not have to know how to construct those services. Instead, the receiving 'client' (object or function) is provided with its dependencies by external code (an 'injector'), which it is not aware of. Dependency injection helps by making implicit dependencies explicit and helps solve the following problems: How can a class be independent from the creation of the objects it depends on? How can an application, and the objects it uses support different configurations? How can the behavior of a piece of code be changed without editing it directly? Fundamentally, dependency injection consists of passing parameters to a method. Because the client does not build or find the service itself, it typically only needs to declare the interfaces of the services it uses, rather than their concrete implementations. This makes it easier to change which services are actually used at runtime, especially in statically-typed languages where changing the underlying objects would otherwise require re-compiling the source code.

Monday, 18 July 2022

Multithreading

In computer architecture, multithreading is the ability of a central processing unit (CPU) (or a single core in a multi-core processor) to provide multiple threads of execution concurrently, which differs from multiprocessing. Threads use the memory of the process they belong to. Inter-process communication is slow as processes have different memory addresses. Inter-thread communication can be faster than inter-process communication because threads of the same process share memory with the process they belong to.

 Where multiprocessing systems include multiple complete processing units in one or more cores, multithreading aims to increase utilization of a single core by using thread-level parallelism, as well as instruction-level parallelism. As the two techniques are complementary, they are combined in nearly all modern systems architectures with multiple multithreading CPUs and with CPUs with multiple multithreading cores.

If a thread gets a lot of cache misses, the other threads can continue taking advantage of the unused computing resources, which may lead to faster overall execution, as these resources would have been idle if only a single thread were executed. Also, if a thread cannot use all the computing resources of the CPU (because instructions depend on each other's result), running another thread may prevent those resources from becoming idle.

Downside is that multiple threads can interfere with each other when sharing hardware resources such as caches or translation lookaside buffers (TLBs). As a result, execution times of a single thread are not improved and can be degraded, even when only one thread is executing, due to lower frequencies or additional pipeline stages that are necessary to accommodate thread-switching hardware.

shared resources (Tanenbaum):

among processes among threads
Address space; global variables; open files; child processes; pending alarms; signals and signal handlers; accounting info; programme counter; registers; stack; state;



From the software standpoint, hardware support for multithreading is more visible to software, requiring more changes to both application programs and operating systems than multiprocessing. Hardware techniques used to support multithreading often parallel the software techniques used for computer multitasking. Thread scheduling is also a major problem in multithreading. 

Threads use the memory of the process they belong to. Inter-process communication is slow as processes have different memory addresses. Inter-thread communication can be faster than inter-process communication because threads of the same process share memory with the process they belong to.



Monday, 4 July 2022

NPIV

 NPIV or N_Port ID Virtualisation is a Fibre Channel feature, consisting of several Fibre Channel node port (N_Port) IDs sharing a single physical N_Port, which lets multiple Fibre Channel initiators occupy a single physical port. This isolates the virtual server's storage from other virtual servers' storage. 

 

N_Port initialization with and without NPIV

Normally N_Port initialisation goes like this:

1- N_Port sends FLOGI to address 0xFFFFFE to obtain a valid address
2- N_Port sends PLOGI to address 0xFFFFFC to register this address with the name server
3- N_Port sends SCR to address 0xFFFFFD to register for state change notifications

When NPIV enters the picture it may proceed like this:

4- N_Port sends FDISC to address 0xFFFFFE to obtain an additional address
5- N_Port sends PLOGI to address 0xFFFFFC to register this additional address with the name server
6- N_Port sends SCR to address 0xFFFFFD to register for state change notifications.


7- ... (repeat FDISC/PLOGI/SCR for next address)

FDISC is an abbreviation for Fabric Discovery, or "Discover Fabric Service Parameters", which is a misnomer. It behaves just like FLOGI.

Sunday, 3 July 2022

Cohesion - a simple explanation

 Cohesion in Java is the Object-Oriented principle that a class should be designed with a single, well-defined purpose. The more focused a class is, the more is the cohesiveness of that class. Having high cohesion ensures that classes are easier to maintain, requiring fewer changes than low-cohesion classes. It also ensure a higher degree of reusability than classes with low cohesion.

example of class without cohesion:

public class Worker (){
String name;
double [] pay;

public void setName (String name){
this.name = name;
}

public String getName(){
return name;
}

public void setPay(double[] pay){
this.pay = pay;
}

public double calculateMeans(){
return means;
}

}

//

The same class with improved cohesion:

public class Worker (){
String name;
double [] pay;

public void setName (String name){
this.name = name;
}

public String getName(){
return name;
}

public class Pay {
Worker worker;
double [] pay;
public Pay (Worker, worker){
this.worker = worker;
}

}



public void setPay(double[] pay){
this.pay = pay;
}

public double calculateMeans(){
return means;
}

}

 

 

Notice the addition of the class Pay, improving the focus of the code and making it more cohesive.