Skip to content

Instantly share code, notes, and snippets.

@mathesond2
Last active August 4, 2021 18:35
Show Gist options
  • Save mathesond2/cee369cf929dc6b6726f4a88add58e8f to your computer and use it in GitHub Desktop.
Save mathesond2/cee369cf929dc6b6726f4a88add58e8f to your computer and use it in GitHub Desktop.
Coursera notes from 'software and architecture' specialization course bundle

design patterns in code = tropes in storytelling (ex: the story pattern of the "heroes journey)

pattern types

creational patterns

  • how you handle creating new objects

structural patterns

  • how objects are attached to each other
  • previously we studied design principles like decomposition and generalization` and how theyre expressed in UML class diagrams with:
  • association, aggregation, composition, inheritance

depending on the relationship of objects you'd like, you can use various design patterns to suit that need.

ex: structural pattern = flavor pairings

  • combo of flavor pairings = a suitable relationship for tasting good
  • some ingredients are mixed together where their flavors are nearly indistinguishable (ex: garlic w chickpeas when making hummus), while others are mixed in where you can still taste distinct flavors but they pair well together (ex: ingredients in a salad), and others are more loosely paired so you dont combine ingredients at all but have the flavors separate because the flavors already complement each other so well (ex: wine and cheese).

the relationships among the food are defined by the pairings. in software, each structural pattern determines the various suitable relationships among the objects.

food pairings = object pairings.

behavioral patterns

this focus on how objects distribute work to accomplish a goal ex: think of a pit crew...each member/object has a specific job, combined they help switch out the car to achieve victory.

like a game plan, a behavioral pattern lays out the overall goal and purpose of each object.

singleton pattern

  • creational pattern
  • "only one object of a class" (ex: "preferences" class in a poker game to determine design and colors of game)
  • is globally accessible
  • the idea of a singleton is that you just want one instance of it, 2 or more would fuck up the pattern (ex: 2 preferences classes means you dont know which class you're using, therefore your preferences in the game may/may not show up...the code becomes unreliable)
  • you can codify this desire to only have one singleton within the singleton itself:
public class ExampleSingleton {
  private static ExampleSingleton uniqueInstance =  null;

  private ExampleSingleton() {
    ... //singleton code..actual content
  }

  public static ExampleSingleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new ExampleSingleton();
    }
    return uniqueInstance;
  }
  ...
}

by hiding the regs constructor and forcing the instantiation of the ExampleSingleton object to go thru getInstance(), we add basic gatekeeping to enforce the fact that there can only be one singleton. Now we dont have to worry about multiple devs creating multiple instances of ExampleSingleton.

  • another benefit of singletons: lazy creation - object isnt created until its actually needed (ex: we dont create ExampleSingleton until we actually need it).

tl;dr: provide global access to a class that is restricted to one instance.

where this is useful: preferences for an app, print queue for your printer, etc

factory pattern method

  • purpose: create objects
  • ex: knife store
  • we have an online store where we sell knives. when someone goes to order a knife, we create a Knife object (this is called 'concrete instantiation'..happens with the new operator).

ex:

Knife orderKnife(String knifetype) {
  Knife knife;

  if knifeType.equals("steak") {
    knife = new SteakKnife();
  } else if knifeType.equals("chefs") {
    knife = new chefsKnife();
  }

  //prep knife
  knife.sharpen();
  knife.polish();
  knife.package();

  return knife
}

now imagine if we get more knives, thats more conditionals, but notice the methods below would stay the same. we can abstract this out into its own factory object to create new knives so theres one dedicated place to create objects of diff types.

public class KnifeStore {
  private knifeFactory factory;
  
  //if someone instantiates KnifeStore, they'll have to provide a factory
  public KnifeStore(KnifeFactory factory) {
      this.factory = factory;
  }

  Knife orderKnife(String knifetype) {
    Knife knife;

    knife = factory.createKnife(knifeType);

    //prep knife
    knife.sharpen();
    knife.polish();
    knife.package();

    return knife
  }
}

^ Here orderKnife is a 'client' of our factory object ,it uses it.

factory object benefits: if theres multiple clients that want to use the same set of classes, but using the factory pattern, you cut out redundant code and make the software easier to modify.

tl;dr: if multiple stores need to create knives, you dont have to re-write that code over and over, plus adding new knives just needs to occur in one place.

in this sense ^, we've generalized orderKnife...this is also called 'coding to an interface, not an implementation'

this above is a FACTORY OBJECT. lets talk about the factory method pattern:

factory method = intent is to define an interface for creating objects, but let the subclasses decide which class to instantiate. ex:

public abstract class KnifeStore {
  public Knife orderKnife(String knifetype) {
    Knife knife;

    //now creating a knife is not a factory, but a method in the class
    knife = createKnife(knifeType);

    knife.sharpen();
    knife.polish();
    knife.package();

    return knife
  }
  
  abstract Knife createKnife(String knifeType)
}

notice here the KnifeStore superclass is now listed as abstract, so it cannot be instantiated as-is, but as a subclass like BudgetKnifeStore or QualityKnifeStore. we've also made createKnife abstract and empty so the subclasses must define them as well.

ex:

public BudgetKnifeSrtore extends KnifeStore {
  Knife createKnife(String knifetype) {
      if knifeType.equals("steak") {
        return new BudgetSteakKnife();
      } else if knifeType.equals("chefs") {
        return new BudgetChefsKnife();
      }
      //more types
      else return null
  }
}

so now we can create many knife stores, but each store has to create its own createKnife method for object creation. This generalizes not just creation of the knife object (like we saw with the factory object before), but the KnifeStore itself becomes generalized.

the factory method pattern ALWAYS goes like this:

  1. a creator superclass which has an abstract factory method that its subclass must instantiate (ex: createKnife)
  2. the creator subclass, known as the "concrete creator" that does "concrete instantiation", creating the factory method that actually create the objects (ex: BudgetKnifeStore).
  3. the concrete product class which is created from the concrete creator class (ex: BudgetSteakKnife).
  4. whatever superclass is used to create the concrete product (ex: Knife)
  • Factory - method or object that allows you to create other objects
  • new keyword - example of 'concrete instantiation' to create actual objects
  • factories allow client code to operate on generalizations, this is called "coding to an interface, not an implementation "
  • factory objects may be useful to create objects and have a single place for them(1st ex), but you can use the factory method pattern to allow concrete creator subclasses to create the actual object by having the method to create the object exist as an abstract method (aka to create the subclass you gotta instantiate the method)

facade pattern

street level front of a starbucks = a facade, indicates what services are available..

facades display whats available, hiding away all the implementation details and extra work that needs to be done.

you dont need to know all the steps to create the coffee, order the beans, etc, you just need to give your order and your money.

facade patterns work like waiters, theyre a wrapper class that encapsulates a subsystem in order to hide its complexity.

ex: a customer class needs to interface with the checking, savings, and investment classes. instead of interacting with all 3 classes and having to know everything about each of the classes you can have another BankService class which the Customer class interfaces with, hiding the details of those 3 classes.

  1. create the interface (ex: IAccount)
  2. impelement the interface w 1 or more classes
public class Checking impelements IAccount {}
public class Saving impelements IAccount {}
public class Investment impelements IAccount {}
  1. implement interface with facade class
  2. use facade class to interact with subsystem.
public class Customer {
  public static void main(String args[]) {
    BankService myBankService = new BankService();

    int mySaving = myBankService.createAccount("saving", new BigDecimal(500.00));
    int myInvestment = myBankService.createAccount("investment", new BigDecimal(1000.00));

    myBankService.transferMoney(mySaving, myInvestment, new BigDecimal(300.00));
  }
}

key design principles this pattern uses: enapsulation, information hiding, separation of concerns

facade design pattern:

  • hides complexity of a subsystem by encapsulating it behind a unifying wrapper called a 'facade class'
  • removes need for client classes to manage a subsystem, delegating that to the facade class itself, decoupling the client classes from the subsystem.
  • acts only as a point of entry into a subsystem.

$ in jQuery is a facade class...you dont need to know about all of the implementation details, you just need to know that you can access jQuery's methods and properties with it.

Adapter pattern

think of the apple adapter to put hdmi to thunderbolt connection (ex: plug in external monitor)

output of one system may not conform to the expected input to another system

this is especially true for your pre-existing system working with 3rd party libraries or needs to speak with other systems.

how this works

  • adapter sits b/w client and adaptee, adapter conforms to the target interface that the client understands, the adapter transforms the request in a way the adaptee understands, and sends the request on over

an adapter class is an example of a wrapper class...it encapsulates the adaptee and presents a new interface that the client can interact with

think of parsers

Composite Pattern

  • composes nested structures of objects and deals with the classes for these objects uniformly.

component interface serves as the super type for a set of classes so they can all be dealt with uniformly

  • ^ this is polymorphism, all implementing classes conform to the same interface.

you can also use an abstract superclass instead of a supertype.

composite pattern is used to address 2 issues:

  • how do we use individual objects to build a tree-like structure
  • how can we treat individual types of objects uniformly without checking their types?

proxy pattern

sometimes its easier to use a placeholder to represent someone else

  • crash dummies instead of ppl used representatives to speak for a co.
  • a credit card is a replaement for cash.

these are proxies...they act as a simple, lightweight version of the original object.

proxy design pattern - proxy class represents subject class.

why use a proxy

  • to act as a virtual proxy - to reduce system load
  • to act as a protection proxy - to protect info in the real subject class
  • to act as a remote proxy - proxy class is local, real class is remote (ex: think of a google doc)

proxy delegates substantial request to object, but can do stuff on its own.

how this works: you have a real object and its proxy object. both objects implement the same interface, achieving what they call 'polymorphism' which basically means since they implement the same interface, you can call the same methods on them regardless of their internal implementations.

the client object interacts with the proxy object instead of the real object.

think of a someone buying something: client object, and warehouse object. client wants something, an order is given to the warehouse. But what if the warehouse doesnt have it in stock? what if the warehouse is too far away to have it shipped in the allotted time? what if there are multiple warehouses?

a proxy can be created, called OrderFulfullment. This proxy class can handle seeing if the item and item amount is actually in stock, this protects the warehouse class from having to know about the client class, and ensures it can stay just doing its own responsibilities and not worrying about logistics. This is also more efficient and thereby less intensive on the system.

steps to create the proxy pattern

  1. design the subject interface
public interface IOrder {
  public void fulfillOrder(Order);
}
  1. implement the real subject class (with the interface)
  2. create the proxy class

proxy pattern characteristics

  • proxy class wraps the real subject class using the same interface
  • ^ has a polymorphic design so that the client class can expect the same interface for the proxy and the real subject class
  • to use a lightweight proxy in place of a resource intensive object until its actually needed.
  • to present a local representation of a system that is not in the same physical or virtual space

this patterns provide a powerful means of indirection, allowing you to build designs that are more protected and less resource-intensive.

Decorator pattern

allows us to dynamically attach behaviors and responsibilities to a class

uses aggregation to combine behaviors at runtime

aggregation - weak relationship ("has a")

think of components A,B, C:

  • 'A' is a base component
  • 'B' augments 'A'
  • 'C' augments 'B'

^ in this case, theyre stacked like CBA

think of base component as coffee...decorators for this are milk,sugar, hot chocolate

why use the decorator pattern?

  • to reduce the # of classes needed to offer a combo of behaviors
  • allows object to dynamically add behaviors to others...you can build functionality by stacking objects wit this pattern.

LOOK AT THE SCREENSHOT

summary

  • decorator lets you embellish your objects by adding any number of behaviors dynamically at runtime using aggregation rather than inheritance
  • aggregation lets us create a stack objects
  • polymorphism is ahieved by impelemnting a single interface
  • each decorator object in the stack is aggregated in a 1-1 relationship with the object below it in the stack
  • by combining aggregation and polymorphism, we can recursively invoke the same behavior down the stack and have the behavior execute upwards from the concrete object

think: stack of turtles: 3. cool sunglasses turtle 2. super turtle

  1. base turtle

3 references 2 references 1, which then builds up the stack of turtles at runtime to create 'cool sunglasses turtle'.

this pattern ensures you dont have to write so many new objects without writing them all out

behavioral patterns

ways objects collaborate to achieve something

ex: each b-ball player has a role, they work together diff ways to win

template module pattern

ex: youre a chef w a chain of italian restaurants, and all recipes have similar steps (boil water, cook pasta, sauce, etc) but are slightly diff depending on recipe. you could put your generalized steps in a class, and have a subclass for the particulars different steps, so you could use fettucini alfredo and pasta bolognese in one recipe, subclassing the particulars.

the main class is the 'template'

ex: pastDish class penneAlfredo subClass spaghetti subClass

public abstract class PastaDish {
  //this is the template method...final keyword= cannot be overridden by subclasses
  public final void makeRecipe() {
    boilWater();
    addPasta();
    cookPasta();
    drainAndPlate();
    addSauce();
    addProtein();
    addGarnish();
  }
  //this is left up to subclasses
  protected abstract void addPasta();
  protected abstract void addSauce();
  protected abstract void addProtein();
  protected abstract void addGarnish();

  private void boilWater() {
    //code
  }
}

public PenneAlfredo extends PastaDish {
  protected addPasta {
    //code 
  }
  protected addSauce {
    //code 
  }
  protected addProtein {
    //code 
  }
  protected addGarnish {
    //code 
  }
}

template method = helpful if you have 2 classes w/ similar functionality

chain of responsibility pattern

  • you visit a dr with a weird problem, they dunno so refer you to a specialist but they dont know so they refer you to another one that actually deals w your problem.

request --> handler --> handler --> handler ^ passing of request continues until one of the objects can fix it.

think of a chain of try/catch blocks...

in software:

click --> handler super class ^ ConcreteHandler1(), ConcreteHandler2()

note: this reminds me of the current shorten_workflow.ts function that sucks.

basic idea:

  1. check if rule matches
  2. if so, do something
  3. if not, call the next function in the list.

theres problems with this pattern...what happens if filter1 doesnt call the next filter?

intent = avoid coupling the sender to the reciever by giving more than one object to handle the request.

state pattern

  • "right now..dance" - the type of dance you do is dependent on what state your body is in (standing/sitting/laying down)

state pattern = an object does stuff based on its current state

this reminds me very much of a react component..do things dependent on current state.

responsibility is given to state, do things based on state ex: interface State

  • insertDollar()
  • ejectMoney()
  • dispense()

state classes (use State interface)

IdleState

  • insertDollar()
  • ejectMoney()
  • dispense() HasOneDollarState
  • insertDollar() - give back 'already has one dollar'
  • ejectMoney() - give back $ and set state as idle
  • dispense() - check stock of item, if theres more than one, dispense item and set vending machine to idle, otherwise dispense item and set item as out of stock OutOfStockState
  • insertDollar()
  • ejectMoney()
  • dispense()

to do all this, we'll have a VendingMachine class

public class VendingMachine {
  private State idleState;
  private State hasOneDollarState;
  private State outOfStockState;

  private State currentState;
  private int count;

  public VendingMachine (int count) {
    //make states
    idleState = new State();
    hasOneDollarState = new hasOneDollarState();
    outOfStockState = new OutOfStockState();

    if (count > 0) {
      currentState = idleState;
    } else {
      currentState = outOfStockState;
    }

    //notice how clean this methods look...no conditionals for the diff states
    insertDollar() {
      currentState.insertDollar(this);
    }
    ejectMoney() {
      currentState.ejectMoney(this);
    }
    dispense() {
      currentState.dispense(this);
    }
    
  }
}

the structure goes like this context object (i.e. VendingMachine)

  • state.handle() (i.e. 'dispense()')

State Interface

  • handle();

ConcreteStateA class

  • handle() ConcreteStateB class
  • handle()

state pattern is useful when you want to change what a method does depending on changings to an object's internal state

command pattern

a commander object puts an intermediary 'command object' between the sender and the receiver to decouple the sender from the reciever....the sender doesnt need to know about how the object actually fulfills the request or by who, it just needs to send something. the command object routes it to the right place

command object has an 'invoker' function that invokes the the receiever object

ex: boss (Sender) needs something done, encapsulates what to do in memos, secretary (Command object) takes these and invokes them by routing them to the workers (receivers)

pattern purposes

  1. store and schedule diff requests
  • store requests into lists
  • manipulate them before they are completed (ex: parsers!)
  • put them on a queue
  1. allowing commands to be undone or redone
  • ex: text editor...you can have a history list and a redo list
    • history list = a list of commands that have been done
    • redo list = a list of commands that have been undone ^ undo a command? look at 'history' list, take most recent command and tell it to undo itself, then putting on the 'redo' list

benefits

  • the commander intermediatary allows the ability to manipulate commands when theyre given (within commander)
  • the commander intermediatary allows the ability to put into undo/redo queues to quickly be able to undo/redo commands
  • decouples sender and receiever from each other, theydont need to know about each other. this allows you also to remove application logic from your UI...ex BitlyServices.ts and calling endpoints...

command pattern decouples the 'what happens' from the 'who does it'

mediator pattern

allows all request to run through a mediator. ex: think of the game object and player1 and player2 objects, with a mediator, you can add as many players as you want because the logic and routing lives within the mediator object itself.

implementation

  • you have mediator and colleague abstract superClasses, then with concrete mediator and concrete colleague(s) subclasses

common usage in software

a user checks a bunch of dropdowns and boxes, and things have to change based on that...instead of those box/dropdown components talking to all those things directly, we have a mediator that manages those diff interactions.

mediator allows loose coupling between colleagues. since all colleagues only speak to the mediator, you can add/subtract as many as you want.

problem = mediator objects can get pretty huge. the benefits of this centralization must be weighed with the downsides of having a large ass, difficult to read object.

observer pattern

you subscribe to a blog, when theres new content, it notifies you.

blog gets a list of observers

  1. have a subject superclass that has 3 methods:
  • allow a new observer to subscribe
  • allow a current observer to unsubscribe
  • notify all observers about new blog post
  • also contains an attribute to keep track of all the observers
  • contains an observer interface so observer that an observer can be notified to update itself
  1. blog class - a subclass of subject superclass
  2. subscriber class -impelements the observer interface

blog object

  • notify() fn
  • update() fn upon notification

subscriber object

  • subscribe() fn to subscribe to blog
  • getState() fn to get current state of blog upon update()
  • unsubscribe() fn

observer pattern is great when you have many objects that rely on the state of one

makes this easy to distribute messages to different parts of a system (or diff systems themselves).

week 3

Design principles underlying design patterns

liskov substitution principle

if a class S is a subtype of class B, then S can be used to replace all instances of B without changing the behaviors of the program.

if S is a subtype of B, its expected that S has all the same behaviors as B, so it could be useful in place of it without affecting software.

open/closed principle

classes should be open to extension but closed to change.

closed = all attrs and behaviors are encapsulated and the class is stable in your system.

this doesnt mean that you can change the class, but just once you've finalized it and finished development, you can close it.

how do you open it though?

  1. inheritance of a superclass...so subclasses get everything from closed superclass
  2. class is abstract and enforces the open/closed principle via polymorphism.

this principle is used to keep the stable parts of your system away from the varying parts. while you wanna be able to add more features to your system, you dont wanna do it at the expense of disrupting something that works. by using extension over change, you can work on the varying parts without introducing unwanted side effects.

isolate the stuff that changes from the stuff that does not.

dependency inversion principle

dependency = coupling

high level modules should depend on high level generalizations and not low level dertails

  • client classes should depend on an interface of abstract class instead of referring to concrete resources.
  • concrete resources shuold have behaviors generalized into an interface or abstract class

is this....coding to an interface?

interfaces and abstract classes = high level resource concrete resources = low level resource

interface/abstract class defines a set of behaviors, concrete classes provide the implementation of the behaviors....

its almost like high level code should deal with the interfaces rather than the details on how it is implemented?

^ all design patterns in this course are based on this principle

ex: bad example

  • --> = depends on

client subsystem --> enterprise subsystem --> backend subsytem ^ low level dependency

ex:

public class ClientSubsystem {
  public QuickSorting enterpriseSorting;

  public void sortInput(List customerList) {
    this.enterpriseSorting.quickSort(customerList);
  }
}

^ now what if we change this sort to a mergeSort? then we have to change this class and its method...this could easily grow out of hand depending on the size of our subsystem and its side effects. what if we just had a generalized 'sorting' interface that we could code to instead?

generalize low-level functionality to interfaces.

good example client subsystem --> IEnterpriseSubsystem interface ... then Actual EnterpriseSubsystem --> IBackendSubsystem interface... which then has backendSubSystem

^ this way we can decouple the subsystems by generalizing their interactions via interfaces, allowing us to switch stuff out easier.

ex:

public class ClientSubsystem {
  public Sorting enterpriseSorting;

  public ClientSubsystem(Sorting concreteSortingClass) {
    this.enterpriseSorting = concreteSorting;
  }

  public void sortInput(List customerList) {
    this.enterpriseSorting.sort(customerList);
  }
}

"dependency inversion principle" improves software systems so client classes become dependent on high level generalized rathen than low level concrete classes

TL;DR code to the idea/interface of something, rather than the specific something, that way those specific somethings can be switched out.

this principle reminds me of bitlyServices...the client hits the action which calls the endpoint via service level fn rather than the client hitting the endpoint directly.

composing objects principle

how to reduce coupling?

indirection =

  • generalization
  • abstraction
  • polymorphism

we wanna get a high amount of code reuse without all the problems of using inheritance.

composing objects principle = classes should achieve code reuse through aggregation rather than inheritance

big time react stuff (decorator design pattern)

"favor composition over inheritance"

look into this more

composing objects principle

  • provide code reuse without using inheritance
  • allow objects to dynamically add behaviors at runtime
  • provide system w more flexibility

interface segregation principle

generalization of concrete classes via interfaces to increase decoupling and allow to change to occur more

if an interface contains too much tho?

interface segregation principle = a class shouldnt be forced to depend on methods it doesnt use...so if youve got a class that implements an interface with 5 methods but uses only 3, it would have to create 'dummy' fns for those remaining 2. this principle says,"hey instead of doing that, why dont you generalize and split up the interface itself?"

Code Smells

general ones

  1. too many comments - may get out of sync, think of it as deodorant for bad smelling code

  2. duplicated code - blocks of similar, but slightly different blocks of code

ex: copy/pasting similar code blocks to diff areas as opposed to having one central location code block lives and letting diff areas hit that location...

if you have the code in one location, updates become very easy.

  1. large methods - maybe theres more in that method than it should be, maybe its too complex and should be broken up?

  2. large classes

  3. data clumps = groups of data appearing together in the isntance of variables of a class or params to methods

instead of singular params, just make an object of those values

  1. long parameter lists

when you're making change to code

  1. divergent change = you have to change a lot of different things within a class for many different reasons...this is poor separation of concerns...class should only have one specific purpose, reducing the amount of changes needed.

if you find that you're having to make tons of changes across a class/component, that could be an indicator that the class/component should be broken up into separate classes/components

  1. "shotgun surgery" - when you have to make a bunch of changes across diff parts of your system to do one thing.

**a change in one place requires you to fix various other areas in the code as a result...**ex: adding a feature, fixing bugs, changing algos, etc.

^ you can normally resolve this by moving methods around, or consolidating them into 1 class (if it makes sense to).

  1. feature envy - theres a method thats more interested in the details of a class than the one that its in

^ if this is the case, just move that method within the other class?

  1. inappropriate intimacy - when 2 classes talk to each other too much

^ factor out methods that both methods use into one class

  1. primitive obsession - reliance on these types too much...they should occur at the lowest level of your code, higher up you should have suitable types/classes for your data...more types = better abstractions

  2. switch statements - big ass conditionals...break the actual things within the conditionals within their own objects/interfaces

  3. speculative generality - aka "we might need this one day"...coding to something you might need later but not now, over-engineering

  4. refused bequest - you inherit a bunch of stuff from a superclass that you dont need.

final project

code smells 1. SectionsPagerAdapter.java

  public Fragment getItem(int position) {
      switch (position) {
          case 0:
              AllItemsFragment all_items_fragment = new AllItemsFragment();
              return all_items_fragment;
          case 1:
              AvailableItemsFragment available_items_fragment = new AvailableItemsFragment();
              return available_items_fragment;
          case 2:
              BorrowedItemsFragment borrowed_items_fragment = new BorrowedItemsFragment();
              return borrowed_items_fragment;
          default:
              return null;
      }
  }

getItem within SectionsPagerAdapter.java is contains a switch statement, which is a code smell as switch statements can be hard to reason about and can grow out of hand over time. To fix this, we can place the code within the cases as an object, either returning the item fragment or null if 'position' parameter provided does not exist as a key within the object

Object obj = {
  0: () {
    AllItemsFragment all_items_fragment = new AllItemsFragment();
    return all_items_fragment;
  },
  1: () {
    AvailableItemsFragment available_items_fragment = new AvailableItemsFragment();
    return available_items_fragment;
  },
  2: () {
    BorrowedItemsFragment borrowed_items_fragment = new BorrowedItemsFragment();
    return borrowed_items_fragment;
  }
}

public Fragment getItem(int position) {
  obj[position]() || return null;
}

public class Dimensions {

private String length;
private String width;
private String height;

public Dimensions(String length, String width, String height) {
    this.length = length;
    this.width = width;
    this.height = height;
}

public String getLength() {
    return length;
}

public String getWidth() {
    return width;
}

public String getHeight() {
    return height;
}

}

Dimensions.java is a code smell is it is a data class that just has an implicit setter within instantiation and getters for length, width, and height. This data class is only instantiated and used within Item.java, so why not combine the Dimensions class within Item.java as a variable? Additionally, having Dimensions as its own class when only being used in one place is an example of the "speculative generality" code smell (ex: "lets break this out into its own class because we might need to eventually").

makes dev easier, provides a strong directions and structure on what needs to be done pms like it as theres useful info to identify risk and manage a project pms should understand risks,coordinate work assignments to change clients - clear architecture communicates that you know what you're doing.

architectures are based on on what style you have in mind

  • ex: data flow = take something and process it through a series of stages, service-perspective arhictecture = independent services work together to achieve something

system architectures are based on what exactly the intention of the software is...whats my app do? figure that out and then you could be helped in deciding out architectural style.

communicating architecture

assessing software

kructen's 4 + 1 views model

Every stakeholder is only interested in some part of software design information. This is an info organizing framework...shows how to look at an architecture from diff perspectives:

  1. logical view - for devs, eng managers
  • captures the functional req of the system, as decomposition of structural elements and/or abstractions.
    • object decomposition - capturing application behavior into classes and packages, translated into UML class or package diagrams
    • data modeling - gathering and organizing data into specific entities, translated into ER Diagrams
    • systems and subsystems - breaking down application into modules/submodules, translated into UML component diagrams
  1. process view
  • captures process, behavior, task concurrency, and data flow. state transition - can be used to understand state and transition - translate via UML State diagrams

data flow - use UML sequence diagrams UML Activity diagrams are useful too

  1. development view
  • team organization
  • dev methodology - agile, waterfall, kanban
  • implementation, programming language chosen determines a lot, libraries, coding style standards, how we test
  • PM stuff - how we track work (ex: jira), feature roadmaps,
  1. physical view
  • captures the infra of an app. it captures hardware mapping of application components or processes (for devops)

uml componenent diagram

visualize how component's diagrams...high level structure, its basically components and their relationships via interfaces

  1. ball connector = shows a provided interface so that others can interact w it

  2. socket connector - shows a required interface

  3. identify main objects

  4. identify libraries you'll be using (ex denote on component diagram)

identifies relationships b/w components without a ton of detail...good early on

I'd actually use this

TLDR - components and their relationships via interfaces

uml package diagram

  • package groups related elements together based on data, classes, or user tasks
  • it also provides a namespace for the elements it contains
  • package diagrams show packages and their dependencies

helpful for a high level look at system to see whats packaged and how unrelated parts do relate to one another somehow

can create and use this whenever in the dev cycle...really useful to see groups of related elements, create a namespace for them, and see their dependencies on other areas.

TLDR - package related elements together, see their dependencies on other packages and describe exactly they are

uml activity diagram

intent: capture the dynamic behavior of the system. here you can pinpoint exactly what activities and conditions should be included, and understand the order in which their included.

represent the control flow from one activity to another in a software system. activities = actions that further flow of execution in your system....ex:

  1. what are the activities?

have start and end nodes (like state diagram).

TLDR - choose your own adventure diagram (yes/no -->).

week 2: architectural styles

language-based systems

OO arch style

when ur choosing a language you're not just choosing a language, like java or c, but a whole arch style: encapsulation/abstraction/decomposition/generalization and also OO patterns are part of the OO arch style.

OO system is focused on the data - how can the data by broken down by abstract data types (think: classes)

OO - system composed of objects, each object is an instance of a class..objects interact with each other via using their methods...this paradigm allows for inheritance amoung abstract data types so one data type can be an extension of another data type.... some problems are well-suited for an OO arch style: ex: easy to identify classes (pet/pet food dispenser/pet collar)

main program and sub-routine style

follows from procedural programming paradigm

promotes modularity and reuse

style focused on functions...when presented with a system to model, you break it up into a main program and sub-routines

its like a call tree...the main program is the top node and the leaves are sub-routines, which can call other sub-routines..

here data is stored as variables. inheritance is not explicitly supported.

main consideration of this style = the behavior of functions and how data moves through them.

sub-routines have data inside their scope, to give subroutines data outside their scope its passed in via params and passed out via return values.

paradigm principal: one entry, one exit per subroutine...makes control flow easy to follow.

promotes modularity and function reuse...when fns are well defined you can think of them as predictable black boxes.

ex: spending mgmt program... reports your spending, keeps track of total you've spent but also which categories your spending falls into

overall functionality of this program can be broken down into subroutines.

main

sub-routines: categorical_spending parse_purchases sum_categories

total_spending display_report

notice categorial_spending subroutine has subroutines within it to further break down the problem

I think of these subroutines just as fns

repo-based systems

databases

diff systems sometimes gotta talk to each other

ex: employees and managers have one system for filling in and approving timesheets, and billing has one system for processing timesheets and pay employees...they gotta talk to each other...how to do it?

issues: state of a component is only existing when its running...how to persist the state/data? how to describe state of components across multiple components?

data-centric software arch = helps increase maintainability/scalability of a system.

shared data storage b/w systems...

2 types of components in this arch style

  1. central data - store/serve data across all components that connect to it
  2. data accessors - components that connect to central data, they make queries and transactions against the info stored in the db.

p simple

dbs

  • have data persistence
  • have data integrity - stays the same doesnt get messed
  • stores in tables (relational)...tables represent an abstraction: ex: employee table, has fields for /firstname/lastname/employeeID/paygrade

relational dbs use SQL to query for info (gimme info) and perform transactions (do something)

data accesors

  • share a set of data but operate indpendently...dont need to know about each other
  • query the db to obtain system info
  • use transactions to save the new state into the system.

^ you can sep concerns into diff data accessors. also, data accessor usage can be controlled so ppl only have access to some.

a centralized db helps out db integrity, backup, data mgigration and so forth. this arch design reduces overhead for data transfer b/w accessors...they all use the db as a way of communicating with each other (meditator pattern)...this allows you to scale accessors as needed

disadvantages:

  • bloated ass DB
  • somethin messes up there, you're screwed.
  • diff to change data scheme

layered systems

layered systems

students teachers principal

^...with these layers, the only interaction occurs within adjacent layers, so principal only interacts with students and students only interact with teachers, while teachers interact w both. also students can interact w each other

ex: if we put 'superintendent' in this layered system, it'll only interact w principal...superintendents dont need shit from students.

a layer = colleciton of components that work together to achieve a common purpose

key point of this system = the only interaction occurs within their own layer or adjacent layers

think of a layer as a single entity.

if upper layer interacts w layer below it, it cannot see any deeper layers (ex: principal could change and students wouldn't know)

bottom layer provides services/abstractions to the layer above...ex: higher layer req info from layer below to provide, and higher level could request an operation from a layer below...a lower level could signal a layer above an event

layering allows for sep of concerns

ex: presentation, data, logic layers

hardware systems are usually based on layers of increasing abstraction from the hardware itself:

ex: computer hardware 3. top layer: utilities and applications - apps are programs, utilities are stuff like tools that come with the computer like a file browser and command line tools...these both rely on system below to use system resources

  1. system/application libraries - providing the higher level fns that programs need to interface w each other, like saving files or drawing graphics

  2. bottom layer kernel - core of an OS (interaces w hardware and allocates resources), ex: schedules operations from processor and reads input from mouse

^ users can perform complex tasks w/o having to understand on the layers below, and programmers can build apps that rely on the libraries layer without having to know about all the details in the kernel

also diff layers can be run at diff levels of authorization....so this arch style lends itself to security well too....the top layer, 'utilities and apps' is typically ran in a 'userspace', which doesnt have the authority to allocate system resources or communicate w hardware directly. this "sandboxing" improves security of kernel

layered arch = more loosely coupled, follows principal of least knowledge...since each layer only interacts w adjacent layers (ex: lower levels layers only supply stuff to layer above)

not all software problems can be mapped to a layered arch tho..

ex: school...principals dont just talk w teachers...they talk w students, parents, etc..

might not to be obvious how to sep. layers

also, if we have a strict layered arch and principal wants to say something to students, principal has to tell teachers, who then relay the msg to students...this extra step adds unnecessary complexity and uses process resources

you have to balance this overhead against the sep of concerns...if you find yourself passing stuff through layers a ton, maybe re-think your arch style to be a more relaxed layered design OR another arch style..

layered designs can be "relaxed" where a layer can talk to a layer not adjacent to it:

students <---- teachers | principal <---

^ here the layers still exist, but students and principal can directly talk to one another, which, for our example..makes sense.

still, this relaxing leads to tighter coupling.

other systems may have more than one grouping in a layer


students |

teachers | support | |

principal |

more complex layers can use 3D layered archs, still only interacting with adjacent elements (insane)

((why do all this?))

  • layered systems are intuitive
  • sep of concerns (each layer is a group of related components w similar responsibility)
  • layered encourages modular, loosely coupled code (switch out layers w/o problems as only adjacent layers touch it)

TLDR: layered arch style are cakes for strong sep of concern and loose coupling, intuitive to understand..ex: presentation/logic/data, students/teachers/principal, utilities and apps/system libraries/kernel

client server n-tier

similar to layered but 'tiers' refer to components on diff physical machines...

theres 3 tier archs, 4 tiers, etc but it can scale up however so we call it 'n-tier'

tiers in an n-tier arch is a client/server relationship....request/response...client req info, server responds..

client-host, server-host - hardware that hosts either software

2 tier arch example: media player (client) 1.1 makes req to view media library 1.3 makes req to play specific video

media server (server) 1.2 respond with media library index 1.4 streams specific video

source of msg isnt necesary for server, so clients can be added as needed (ex: I sign into netflix in my phone as well as my ipad in addition to the TV example above.)

imagine you have 3 computers (clients) and a db (server), to make a 2 -tier arch. if one of your clients reqs a DB change, this will have to change on all 3 computers...this can be a problem

you can have a tier b/w the client and the server to fix this...this additional layer is called the application/business layer. It's a client for the db and a server for the client app on the end user's devices

media player business layer - determines how/when data can be changed and in what ways media server

adding this middle layer means the client and sercer layers can be thinner, b/c they dont need to worry about this stuff, further enforcing sep of concerns.

client layer/tier application layer/tier server layer/tier

drawbacks to this arch:

  • adding more tiers means more processes to manage
  • can make a system more
  • server can be a single point of failure...redundant servers are possible but add complexity.

advantages:

  • scalable
  • centralizing functionaltiy - db that needs to be accessed by a number of clients
  • centralized computing power - users dont have that computing power on their own machines so some comapnies exist to provide that computing power

TLDR: client/server n-tier arch styles...tiers refers to components on diff physical machines. you can add tiers based on software need, and additional tiers can be needed to allow other tiers to be thinner and enforce the sep of concerns (ex: client/application layer/server).

interpreter-based systems

interpreters

  • interpreter = interprets commands from a user...kinda like excel...interpreters allow users to write scripts, macros, and rules that access, compose, and run the basic features of that system in new ways. enables ppl to write their own programs...

scripts - automate common tasks macros - records keyboard/mouse inputs so they can be executed later rules - extend functionality of system sum(A1:A4)...

ex: chrome extension = adds new functionality to browser, ran by an interpreter embedded in the browser...

interpreters encourage users to implement their own customizations..this is cool b/c it creates a domain-specific languagge (an abstration) so that way end users can work in the way they know or one thats intuitive to them as opposed to having to write actual programmer language code.

besides building an interpreter into a system, a system itself can be implemented in a language that depends on an interpreter, this can make a system more port because it can run on any platform that supports the interpreter to make your system more platform independent...

interpreter and its env abstracts the underlying platforms to make the system more platform independent.

portability = v important w rise of VMs and more service put into the cloud

ex:

  • java programs are translated into an intermediate language,
  • intermediate code loaded into a JVM (java virtual machine)
  • JVM executes the intermediate language, trying to optimize intermediate instructions by monitoring the freq by which stuff is executed frequently...this stuff gets bumped into machine code and executed immediately.

do interpreters work w JS?

interpreters

  • allow scripting
  • creation of macros
  • portability of systems across various platforms

Dataflow systems

pipes and filter arch

data flow arch = "a system is just a series of transformation on sets of data"

filters - perform transformations on data it gets pipes - connectors

this is obvious...I think of the decorator pattern w this, as order matters.

uml modeling - component diagram makes sense

this reminds me of functional programming basically...allows loose coupling and self-contained units. filters treated as black boxes.

implicit invocation systems

event based

fundamental elements are events: users inputs, messages, signals, data from other fns/programs

events are indicators of change in the system and fns

fns = event generaters and event consumers

weird..fns dont directly call ecah other but do it thru an 'event bus'

each event consumer consumer registers with the event bus. when event bus hears of it, it notifies the event consumer.

this is a direct implementation of the observer pattern.

how to construct this? have a main loop that listens for events, when one is detected, it calls all fns bound to that event.

ex: code editor
  1. after editing a file, save button is clicked. save click generates an event from editor to the event bus to say that project is done and ready to be re-built.

  2. build tool component consumes 'save' event and on completion is triggered by this event to process the file. after completing, it generate 'build status' event and 'run tests' event to event bus

  3. test tool component consumes 'run tests' event and on completion generates a 'test completion' event

  4. editor component consumes 'test completion' event from event bus...editor presents the latest test status.

event-based: event generators dont know how will consuming their events, and event consumers dont where the events come from...super loose coupling, good for scaling.

race conditions - behavior of fns depend on the order in which they're called.

semaphore - common technique in event architectures to indicate whether or not a particular resource is being accessed by another process at the moment.

pub-sub style

all components are publishers or subscribers, they cant be both. like event-based style, it relies on implicit invocation, encouraging loose coupling.

simple implementation: to receieve msgs, subscriber registers their interest to publishers thru a callback. publishers have list of subscribers and communicate to them via calls to the registered callbacks.

process control systems

basic ex: feedback loop...ex: a room, has thermostat

  1. sensor - monitors some imp info (thermostat)
  2. controller - logic -ie the software
  3. actuator - the physical method of manipulating the process (heating vent)
  4. process - what you're trying to control (room)

sensor reads temp, too chilly sensor sends data to software (controller) software computes diff b/w data and desesired temp, tells the heating vent to open eventually room heats up to desired temp sensor reads this, relays data to software software sees its all good, tells heating vent to close

^ this is the feedback loop.

rate of feedback loop is variable based on need (ex: feedback loop on thermostat vs. self-driving car)

week 3

quality attributes

how are archs evaluated?

arch = combines design patterns and principles to define the software elements and how they interact w each other

arch is not just design patterns and principles tho

  • design patterns are good at addressing specific technical problems, but poor at addressing a wide set of business needs. system arch is more concerned w the bigger picture (functional and non-functional)...it gives guidelines design patterns within it for conceptual integrity

ex: arch must consider non-functional reqs = testability, availability...design patterns dont address this. they also dont concern ease of development and user experience

arch = designed to address a set of requirements, requirements are used to address a problem/need

  • diff archs are needed for diff concerns
  • typically, your system will use a combo of architectural designs...your problem will often by multi-facted and arch styles arent a one-size-fits-all

non-functional reqs - ex: dev team cares about reusability, maintainability,testability, end user cares about ease of use, err handling, and sysetm stability...diff stakeholders have diff expectations.

how do you meaure this? quality attrs...metrics for evaluating archs:

  • maintainability - how easy system can undergo change
  • flexibility - how can it adapt to future reqs?
  • reuseability - take parts of a subsystem and use elsewhere
  • modifiability - how well can it be modified (ex: adding new tech)
  • testability - how easy is it to demonstrate errs thru executable tests

also attrs from user perspective

  • availability - uptime
  • interoperability - how well can it recognize and respond to other systems?
  • security - from un auth'ed use
  • performance - how well system responds to user inputs or system event, measured by systems throughput (amount of output produced over time) and latency (how long to produce an output given an input)
  • usability - how well it addresses the needs of users

you need good docs for your arch

use a set of rules for the arch design process and how your system will be structured, here's some general rules:

  • recognizing importance of quality attrs and prioritizing them for each system being designed
  • involving a technical lead in the design process
  • taking a design approach from diff stakeholders
  • create structural rules for conceptual integrity:
  • well defined subsystems that are assigned responsibilities based on design principles (sep of concerns, information hiding)
  • having consistent implementations of fns across the entire system
  • having a set of rules on how resources are used (ex: memory, bandwidth, threads)

use miro to create UML diagram templates

analyzing/evaluating an arch

designed to address biz needs of various stakeholders, but is harder to evaluate bc software is more abstract than say, building a house. quality attrs - measures a functional/non-functional req of a system

Q: but how to actually measure these quality attrs?

answer: quality attr scenarios:

  • general - for any system
  • concrete - for a specific system

goes like this: stimulus source --> stimulus --> artifact (environment) --> response --> response measure

stimulus source - anything that creates a stimulus, ex: internal timer stimulus - a condition that will cause the system to respond (ex incorrect user input) environment - mode of system when recieving stimulus artifact - part of system that is affected by stimulus response - how the artifact will behave as a result of receiving stimulus response measure - metric to quanity the response so that the quality attr can be measured

^^^ scenarios - focus on situations outside of the happy path ex: security breaches, incorrect input, heavy system loads

ex: availability, you dont want to measure when the system is online and behaving normally...you need to consider the situations that cause the system to become unavailable, so you should construct scenarios to cause your system to become unavailable and then measure how long it takes to recover

ex: netflix chaos monkey

concrete scenarios - test an arch with a specific stimulus under specific system envs, and measure how well a system will respond.

ex: measure servers availability under specific conditions.

source: customer stimulus: req to purchase show tickets artifact: web service env: maximum process limit reached response: inform customer that service is busy response measure: time to complete current requests

ATAM - Architecture Tradeoff Analysis method ^ evaluators dont need to be familiar w arch or problem space...so you dont even have to be in the space to evaluate the arch...whaaa.

product arch

relationship to org structure

conway's law - a system will tend to take a form that is congruous to the organization that produced it

ex: web app - you have a n-tier arch w 3 tiers: 1 team builds data backend, 1 team builds application logic layer, 1 team builds presentation tier

product lines

iphone,ipad = a product line, all live on iOS, though they have diff functions (phones have to connect to cell towers)

product lines can help create less of a learning curve, ux-wise, and increase time-to-market (ex: home products using iOS)....this is why code reusability is so important.

creating a product line

  1. separate the features that stay the same from the features that are diff across products (ex: iOS system)
  • commonalities - features that stay the same (ex: ux)
  • variations - features of product line that vary b/w products (ex: tablets, support for diff cell connections)
  • product specifics - features belonging to just 1 product (ex: 1 tablet specialized for ebooks, has additional tools for e-book mgmt)

product lines allows a lot more re-use...this occurs through splitting eng from domain engineering to application engineering

software design - lower level aspects of system architect - higher level aspects

software design is the study of boxes and lines...things and their relationships

we typically communicate software design through words and diagrams

principles:

  • simplicity first

object oriented modeling allows like-minded things to stick together, and also helps us understand how components are composed and sub-composed.

ex: seat - has cushion details, color, size, etc

also, an object knows its properties (I know my fav color, what I like to eat, do, etc)...an object is self-aware.

SOFTWARE REQUIREMENTS CRITICAL...this is learning to ask about tradeoffs, see exactly what someone is looking for...

think like an architecture = think about structure and behavior

ex: "should a house be facing a specific direction?" "does it have external constraints?" "how big is the house?"

conceptual design = here you map out the COMPONENTS, their CONNECTIONS, and all their associated RESPONSIBILITIES

ex: a floor plan...this is a rough draft, clearer the conceptual design, better the technical design.

technical design = here you design HOW these RESPONSIBILITIES ARE MET, specifiying the technical details of each component by splitting components into smaller components thatre specific enough to be designed in detail:

ex: gym - gym needs a v strong floor

technical diagrams help w this.

sometimes in the technical design process you uncover flaw (ex: need super strong floor supported by steel beams underneath but client wants an open floor plan in basement), that you need to flesh out w client...this is a good time for this

components turn collections into functions, classes or other components...they represent a much simpler problem devs can implement.

its all components and connections

user stories as a ___ I want to ____ so that ___.

^ user stories are a technique to express requirements to a software system.

we can break objects down into other objects...theres 3 kinds of objects

  1. entity objects - correspond to some real world entity in the problem space (ex: an object representing a user/customer). generally these objects will know attributes about themselves and have rules to do so.
  • when breaking down objects into other objects, you'll initially get entity objects...the other objects come later as you start to think about the technical design of the software.
  1. boundary objects - objects that sit at the boundary b/w systems ex: object that deal w a 3rd party system (like elasticSearch), OR an object that gets info from a user (ex inputfield)...UI stuff is mostly boundary objects.
  • Any object that deals with another system (a user/the internet/some 3rd party code) is a boundary object
  1. Control objects - responsible for coordination (think the mediator pattern, which coordinates the activities of other objects so they may stay loosely coupled...the button mashing game example)

organizing your software into entity/boundary/and countrol objects will allow your code to be more usable and maintainable over time.

1.1.5 competing qualities and tradeoffs

when designing software, you have to continuously think about convenience, performance, and security

the job of the architect is to ensure the quality, and also defining what that quality is. "this is our goal, this is how we define it, and this how we ensure it."

(class, responsibility, collaborator) CRC cards - help to organizes components into classes...used to organize your components into classes, identify the responsibilities and determine how they will collaborate with each other.

  • class name - top part of card
  • responsibilities - left part - what it does
  • collaborators - right part - the other classes the class interacts with to fufill its responsibilities

class name = Bank Customer responsibilties - insert bank card, choose operation collaborator - bank machine

class name = Bank machine responsibilties - auth customer, display task options, deposit/withdraw, check balance collaborator - bank customer

how does a bank machine auth a user? create a new card, Bank class.

But also, you'll need a network, so you'll create a Network card, and you want that network to be secure, so you'll create an encryption card

  • class name = coffee customer

  • responsibilities = asking for menu item, inserting bank card or giving cash, receieving change, recieving menu item

  • collaborators = card reader, barista

  • class name = barista

  • responsibilities = taking customer order, entering menu items on computer, asking for payment, dispensing change, composing menu item, dispensing menu item

  • collaborators = cash register, coffee customer, espresso machine, pastry case

  • class name = pastry case

  • responsibilities = holding pastries for freshness, customer display

  • collaborators = barista

  • class name = espresso machine

  • responsibilities = taking in (milk, water, espresso beans) and allowing barista to create espresso drinks

  • collaborators = barista, plumbing network

  • class name = plumbing network

  • responsibilities = provide water to building facilities

  • collaborators = espresso machine

  • class name = cash register

  • responsibilities = tracking items sold, notifying user of payment total including tax, sending reports to external reporting system

  • collaborators = barista

  • class name = external reporting system

  • responsibilities = tracking what has been sold during a timeframe

  • collaborators = manager

  • class name = manager

  • responsibilities = taking care of operational side of business, hiring, inventory maintenance, etc

  • collaborators = external reporting system

  • class name = card reader

  • responsibilities = taking card, interfacing with external system, asking user if credit/debit, asking user for pin #, notifying user of payment acceptance, denial

  • collaborators = coffee customer

  • class name = external payment system

  • responsibilities = processing payments

  • collaborators = card reader

Module 2

deeping deeper into object-oriented modeling

imperative - cobol and fortran were imperative programming languages that broke up large tasks into subroutines, kinda like methods on a class. since processing time was mad $$ it was importatnto maximize processing performance...this was accomplished by having global data in one place so all subroutines can occcur...but of course, this leads to global state problems

global state problems led to pascal, which now had local variables. they introduced abstract data types, which is a group of related info with a type (not created by the lang!!)...its a way to organize data in a meaningful way

in 70's, processing time was cheap but ppl were $$...C and Modula 2 let programs to be separated into separate files, and also imports, called a 'header file'.

what about inheritance? this is where java comes in...OOP's goal is to

  • make an abstract data type easier to write
  • structure a system around abstract data types called classes
  • introduce ability for abstract data type to inherit another

the good thing about an OOP lang is the structure of the lang can reflect the structure of the problem.

abstraction

its how we humans deal w complexity....we simplify a concept in the problem domain to its essentials within some context...this allows us to better understand a concept by breaking it down and ignoring unimportant details.

ex: demoselle's d'avignon

an abstraction should make sense for its concept's purpose, following 'the rule of least astonishment':

the rule of least astonishment

the abstraction captures the essential attrs and behavior of a concept w no suprises and/or definitions that fall beyond its scope (aka no irrelevant shit)

a class is the model of a concept (ex: a person)

abstractions are formed within a SPECIFIC CONTEXT!!

encapsulation

3 ideas

  • self-contained object, w attrs and behaviors
  • exposes an interface to access it
  • restricts access to certain details

programmings just easier when data and methods that manipulate that data are in the same place

encapsulation helps with changes in software because the other objects using an object's methods dont need to know the internals of it, they just need to be able to use the same interface to access the functionality they need

blackbox thinking - you dont know how something is done, but there's inputs to the box and gives outputs

encapsulation achieves an 'abstraction barrier' since the internal workings of an encapsulated piece of software arent relevant to the outside world.

Decomposition

taking a thing and dividing it up into parts OR taking a bunch of separate parts and putting them together to understand/solve. the idea is to be able to break down problems into pieces are are easier to understand and solve.

general rule: look at the responsibilities of a whole thing and see who you can break it down int oits parts

decomposition lets you create clearly defined parts

generalization

helps reduce redundancy in solving problems...ex iterate through collection via map()

generalization can be achieved via inheritance...the generic stuff is in the parent class and the child class inherits its generalized attrs/behaviors

parent/child also referred as superclass/subclass

ex: cat dog subclasses inherit from 'animal' superclass

inheritance and methods are the hallmark of generalization and generalization is a HUGE part of DRY.

UML diagrams

  1. class diagram = includes class name, attributes, operations...good for implementation, but not conceptual

encapsulation in java and UML

  • encapsulation = the data and the functionalities that manipulate that data reside in the same place (think context of state mgmt)

3 ideas:

  • bundle data and fns that manpulate it
  • allow outsider objects to use certain pieces of data/functionality
  • restrict access to certain data/functions within that object

"-" = private "+" = public

public class Student {
  private float gpa;
  private String degreeProgram;

  public float getGPA() {
    return gpa;
  }
  public v
  public string getDegreeProgram() {
    return degreeProgram;
  }
}

getter methods - often retrieve a private piece of data setter methods - often set a private attribute in a safe way data safety is why we have these ^

decomposition in java and UML

3 types

  1. association - a loose relationship
  • in class diagram notation = a straight line b/w 2 objects and 0..* on both sides of the line, stating that the object is associated with "0 or more" of the other type of object

ex in code:

public class Food {
  public void pair(Wine wine) {

  }
}

^ the relationship here is a loose one w food and wine...it's relationship is within the pair function but wine lives as a parameter and can exist outside of the class.

  1. aggregation - a 'has-a' relationship...ex: airliner and crew --> they have a relationship with each other, but can exist independent of them. ex: "bookshelf and books".

in UML notation: straight line with 0..* on both sides, but the consuming class (in the case of airliner and crew, it would be airliner), gets a diamond at the end of the line touching it.

^ the airliner is considered "the whole" and the crew is considered "the part".

ex in code:

private class PetStore {
  private ArrayList<Pet> pets;

  public PetStore() {
    pets = new ArrayList<Pet>();
  }

  public void add( Pet pet){...}
}

^ both pet store and pets can exist without each other

  1. composition - a strong 'has-a' relationship with its parts, aka the whole cannot live without its parts

UML diagrams = two lines, 'whole' class has black diamond, and 'part' class has 1..* (1 or more) attached to it, stating that there may be 1 or more instances of that class. ex: house -- room, there may be 1 or more rooms to a house, so house is the 'whole' class and room is the 'part' class.

ex: composition using java

public class Human {

  private Brain brain;

  public Human() {
    brain = new Brain();
  }
}

^ brain is created immediately when Human is created.

another example of composition: employee and salary

public class Employee{
  private Salary salary;
  
  public Employee(Salary employeeSalary){
    this.salary = employeeSalary;
  }
}

Inheritance with java and UML

  • connect 2 classes with a solid lined arrow --|>, this indicates two classes are together via inheritance..the superclass is at the head of the arrow and the subclass is the tail....superclasses always on top, subclasses always on bottom

a subclass inherits everything from its superclass, including private variables

// abstract = since an animal is a generalization of a specific species,
// you cant create an animal object on its own...we use the "abstract" keyword to denote that you cannot instantiate this class...animal class will be superclass for dog subclass
public abstract class Animal {
  protected int numberOfLegs;
  protected int numberOfTails;
  protected String name;

  public Animal(String petname int legs, int tails) {
    this.name = petName;
    this.numberOfLegs = legs;
    this.numberOfTails = tails;
  }

 public void walk(){...}
 public void run(){...}
 public void eat(){...}
}

subclass ex

//'extends' denotes inheritance
public class Dog extends Animal {
  public Dog(string name, int legs, int tails) {
    super(name, int, tails);
  }

  public void playFetch(){...}
}

you instantiate objects from a class by using constructors...you can either have implicit constructors (in the case of the Animal class) or explicit constructors in the case of Dog..implicit constructors all have null values of its properties. so in the case of Animal, numberOfLegs will be intially null.

explicit constructors allow us to sign values during instantiation (ex super(name, int, tails)) a subclass must call its superclass constructor if the superclass has an explicit constructor

super(); <- we hit the superclasses constructor and the values of the params allow us to override the initial superclass values

types of inheritance

  • implementation inheritance - in java, only this is allowed...a superclass can have multiple subclasses, but a subclass can only inherit from one superclass

generalization with interfaces in Java and UML

an interface is a contract to be fufilled by implementing classes.

interfaces are not classes, theyre used to describe behaviors, they only have method signatures.

public interface IAnimal {
  public void move();
  public void speak();
  public void eat();
}

^ we dont show how these behaviors are performed, we only state that the animal has these behaviors. also notice theres no attributes here, this is because interfaces only denote BEHAVIORS.

now how do we actually use this in java? the implements keyword this states that our class will agree by the standards put in the interface

// notice the 'I' denoting an interface
public class Dog implements IAnimal {
  /*dog atts*/
  public void move() {...}
  public void speak() {...}
  public void eat() {...}
}

^ note when you implement an interface you HAVE to have all of those behaviors mentioned in the interface.

in UML = <<interface name>>, theres a dotted arrow, the head of the arrow touches the interface and the tail is the class that implements it.

advantages of interfaces:

  • allow you to implement polymorphism = 2 implementations of the same behavior (ex: cat and dog class, both use IAnimal interface, but both speak differently).

polymorphism = the description of the behavior is the same, but the implementations are different.

in java, a class can inherit as many interfaces as it needs. still, fuckin weird.

public interface IPublicSpeaking {
  public void givePresentation();
  public void speak();
}

public interface IPrivateConversation {
  public void lowerVoiceVolume();
  public void speak();
}

public class Person implements IPublicSpeaking, IPrivateConversation {
  public void speak() {
    //...
  };
}

interfaces allow you to describe behaviors without impmlementing them

“Draw a UML class diagram of an Object-Oriented model for a car rental company that keeps track of cars, renters and renters renting cars ”

Create a UML class diagram to represent this information. Showing the correct classes and relationships is enough. Do not add attributes or methods to the classes.

Hint: You may want to make a class for "renters renting cars".

CarRentalCompany

Cars

Renters

RentersRentingCars

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment