an object cannot or does not want to know the identity of the object whose method it calls.
an object does not want to know the effect of the method call.
uses of publisher-subscribers:
enables building reusable components
facilitate separation of business logic (concerns, responsibilities)
Centralized vs. decentralized execution/program-control spreads responsibilities for better balancing.
Decentralized control does not necessarily imply concurrent threads of execution.
steps:
Information source acquires information in some way and we assume that this information is important for other objects to do the work they are designed for.
Once the source acquires information (becomes “information expert”).
The information expert then publishes the information to the subscribers.
information experts are usually called “publishers”.
// Subscriber interfacepublicinterfaceSubscriber{publicvoidreceive(Contentcontent);}// Content or event schema that needs to be published by publishers, received by subscribers// both Publisher and Subscriber should know about this schema// A Content object contains only data, no business logic, and is meant to transfer data from Publisher to Subscriber.importjava.util.ArrayList;publicclassContent{publicPublishersource_;publicArrayListdata_;publicContent(Publishersrc,ArrayListdat){source_=src;data_=(ArrayList)dat.clone();// for write safety avoid aliasing and create a new copy}}// Publisher interfacepublicinterfacePublisher{publicsubscribe(Subscribersubscriber);publicunsubscribe(Subscribersubscriber);}// Subscriber classpublicclassSubscriberClassimplementsSubscriber{publicconstructor(PublisherpublisherObject){publisherObject.subscribe(this);// subscribe to publisher upon creation}publicvoidreceive(Contentcontent){if(content.source_==instanceofPublisherClass){// do something with the content that comes from the publisher that we subscribed to}// other content that we don't care about}}// Publisher classpublicclassPublisherClassimplementsPublisher{protectedArrayList<Subscriber>subscribers_=newArrayList<Subscriber>();publicvoidpublish(Contentcontent){for(Subscribersubscriber:subscribers_){subscriber.receive(content);}}publicvoidsubscribe(Subscribersubscriber){subscribers_.add(subscriber);}publicvoidunsubscribe(Subscribersubscriber){subscribers_.remove(subscriber);}publicnotifySubscribers(String:message){Contentcontent=newContent(this,message);publish(content);}}
subscriber may subscribe to multiple publishers:
subscriber needs to check the source of the content before doing any action.
the subscriber.receive(content) method now contains multiple nested if statements checking for source, then content type.
to avoid this, we can use polymorphism, so instead of having one subscriber, we can have multiple subscribers each is specialized for a different type of content.
the code below represents this case, and can be improved in a different ways.
we have now two subscribers, each specialized for a different type of content.
the tradeoff is that the publisher code is more complicated.
// Publisher classpublicclassPublisherClassimplementsPublisher{protectedArrayList<Subscriber>subscribersOfEvent1_=newArrayList<Subscriber>();protectedArrayList<Subscriber>subscribersOfEvent2_=newArrayList<Subscriber>();publicvoidpublishEvent1(Contentcontent){for(SubscribersubscribersOfEvent1_:subscribers_){subscriber.receiveEvent1(content);}}publicvoidpublishEvent2(Contentcontent){for(SubscribersubscribersOfEvent2_:subscribers_){subscriber.receiveEvent2(content);}}publicvoidsubscribeEvent1(Subscribersubscriber){subscribersEvent1_.add(subscriber);}publicvoidsubscribeEvent2(Subscribersubscriber){subscribersEvent2_.add(subscriber);}publicvoidunsubscribeEvent1(Subscribersubscriber){subscribersEvent1_.remove(subscriber);}publicvoidunsubscribeEvent2(Subscribersubscriber){subscribersEvent2_.remove(subscriber);}publicvoidnotifySubscribers(String:message){Contentcontent=newContent(this,message);if(message=="event1"){publishEvent1(content);}elseif(message=="event2"){publishEvent2(content);}}}// Subscriber classpublicclassSubscriberClass1implementsSubscriber{publicconstructor(PublisherpublisherObject){publisherObject.subscribeEvent1(this);// subscribe to publisher upon creation}publicvoidreceiveEvent1(Contentcontent){if(content.source_==instanceofPublisherClass&&content.data_=="event1"){// do something with the content that comes from the publisher that we subscribed to// event1 type}}}publicclassSubscriberClass2implementsSubscriber{publicconstructor(PublisherpublisherObject){publisherObject.subscribeEvent2(this);}publicvoidreceiveEvent2(Contentcontent){if(content.source_==instanceofPublisherClass&&content.data_=="event2"){// do something with the content that comes from the publisher that we subscribed to// event2 type}}}
The Publisher-Subscriber design pattern is used in the Java AWT and Swing tool kits for notification of the GUI interface components about user generated events.
the publisher-subscriber is known in java as Source-Listener or delegation event model.
making a component independent of others makes it reusable
In the “ideal” case, all objects could be made self-contained and thus reusable by applying the pub-sub design pattern.
trade offs:
indirect communication requires much more code, which results in increased demand for memory and decreased performance.
if it is not likely that a component will need to be reused or if performance is critical, direct communication should be applied and the pub-sub pattern should be avoided.
Publisher-Subscriber belongs to the category of behavioral design patterns.
behavioral design patterns: separate the interdependent behavior of objects from the objects themselves, they separate functionality from the object to which the functionality applies.
the invoking object (client) needs to reverse the effect of a previous method invocation.
the ability to trace the course of the system operation.
The purpose of the Command patter is to delegate the functionality associated with rolling back the server object’s state and logging the history of the system operation away from the client object to the Command object.
client => command => server (receiver), so the client does not talk directly to the server, instead the talk to the Command which in tur delegate the task to the server.
Command Class:
represents operations as classes and is used whenever a method call alone is not sufficient.
is the central player in the Command pattern,
CommandHistory maintains history log of Commands in linear sequence of their execution
It is common to use Command pattern in operating across the Internet. For example, you send an http request with arguments to the server, the http server (Command) receives the request invokes some method on the request, then returns the result to the client into an http response.
Undo/Redo:
Command pattern optionally supports undo and redo functionality (rollback).
CommandHistory should respond properly to undo/redo requests.
The Decorator pattern is used to add non-essential behavior to key objects in a software design.
The embellished class (or, decoratee) is wrapped up by an arbitrary number of Decorator classes, which provide special-case behaviors (embellishments).
the Decorator is an abstract class (the class and method names are italicized). The reason for this choice is to collect the common things from all different decorators into a base decorator class.
In this case, the Decorator class will contain a reference to the next decorator.
The decorators are linked in a chain.
The client has a reference to the start of the chain and the chain is terminated by the real subject.
The State design pattern is usually used when an object’s behavior depends on its state in a complex way
In this case, the state determines a mode of operation.
The State pattern externalizes the relevant attributes into a State object, and this State object has the responsibility of managing the state transitions of the original object.
The original object is called “Context” and its attributes are externalized into a State object
Each concrete State class implements the behavior of the Context associated with the state implemented by this State class.
classContext{privateStatestate;publicContext(StatedefaultState){this.state=defaultState;}publicvoidrequest(){this.state.handle(this);}publicvoidchangeState(StatenewState){this.state=newState;}}classState{publicvoidhandle(Contextcontext){// do nothing}}classConcreteStateAextendsState{publicvoidhandle(Contextcontext){// do somethinglog("ConcreteStateA");}}classConcreteStateBextendsState{publicvoidhandle(Contextcontext){// do somethinglog("ConcreteStateB");}}ConcreteStateAstateA=newConcreteStateA();ConcreteStateBstateB=newConcreteStateB();Contextcontext=newContext(StateA);context.request();// ConcreteStateAcontext.changeState(StateB);context.request();// ConcreteStateB
The Proxy pattern is used to manage or control access to an object.
Proxy is needed when the logistics of accessing the subject’s services is overly complex and comparable or greater in size than that of client’s primary responsibility.
In such cases, we introduce a helper object (called “proxy”) for management of the subject invocation.
A Proxy object is a surrogate that acts as a stand-in for the actual subject, and controls or enhances the access to it
The proxy object forwards requests to the subject when appropriate, depending on whether the constraint of the proxy is satisfied.
The causes of access complexity and the associated constraints include:
The subject is located in a remote address space, e.g., on a remote host, in which case the invocation (sending messages to it) requires following complex networking protocols. Solution: use the Remote Proxy pattern for crossing the barrier between different memory spaces.
Different access policies constrain the access to the subject. Security policies require that access is provided only to the authorized clients, filtering out others. Safety policies may impose an upper limit on the number of simultaneous accesses to the subject. Solution: use the Protection Proxy pattern for additional housekeeping
Deferred instantiation of the subject, to speed up the performance (provided that its full functionality may not be immediately necessary). For example, a graphics editor can be started faster if the graphical elements outside the initial view are not loaded until they are needed;
only if and when the user changes the viewpoint, the missing graphics will be loaded. Graphical proxies make this process transparent for the rest of the program. Solution: use the Virtual Proxy pattern for optimization in object creation
In essence we could say that proxy allows client objects to cross a barrier to server objects
The barrier may be:
physical (such as network between the client and server computers).
imposed (such as security policies to prevent unauthorized access).
As a result, the client cannot or should not access the server by a simple method call as when the barrier does not exist.
The additional functionality needed to cross the barrier is extraneous to the client’s business logic.
the client has an illusion it is directly communicating with the subject, and does not know that there is a barrier in the middle which is the proxy.
Proxy offers the same interface (set of methods and their signatures) as the real subject and ensures correct access to the real subject.
Because of the identical interface, the client does not need to change its calling behavior and syntax from that which it would use if there were no barrier involved.
In event-driven applications, such as graphical user interfaces, the user expects a quick response from the system.
If the (single processor) system processes all requests sequentially, then it will respond with significant delays and most of the requestors will be unhappy
to handle this, processors use time-sharing or time slicing: a single processor dedicates a small amount of time for each task, so all of them move forward collectively by taking turns on the processor.
Although none of the tasks progresses as fast as it would if it were alone, none of them has to wait as long as it could have if the processing were performed sequentially.
The task executions are really sequential but interleaved with each other, so they virtually appear as concurrent.
real concurrency when the system has multiple processors, and virtual concurrency on a single-processor system.
Middleware is a good software engineering practice that should be applied any time the communication between objects becomes complex and starts rivaling the object’s business logic in terms of the implementation code size
Middleware is a collection of objects that offer a set of services related to object communication, so that extraneous functionality is offloaded to the middleware.
Information security is a nonfunctional property of the system, it is an emergent property.
there are two main security disciplines:
Communication security is concerned with protecting information when it is being transported between different systems.
Computer security is related to protecting information within a single system, where it can be stored, accessed, and processed
The main objectives of information security are:
Confidentiality: ensuring that information is not disclosed or revealed to unauthorized persons
Integrity: ensuring consistency of data, in particular, preventing unauthorized creation, modification, or destruction of data
Availability: ensuring that legitimate users are not unduly denied access to resources, including information resources, computing resources, and communication resources
Authorized use: ensuring that resources are not used by unauthorized persons or in unauthorized ways
To achieve these objectives, we institute various safeguards:
concealing (encryption) confidential information so that its meaning is hidden from spying eyes
key management which involves secure distribution and handling of the “keys” used for encryption.
steps:
a sending computer encrypts the original data using an encryption algorithm to make it unintelligible to any intruder
The data in the original form is known as plaintext or cleartext. The encrypted message is known as ciphertext.
Without a corresponding “decoder,” the transmitted information (ciphertext) would remain scrambled and be unusable.
The receiving computer must regenerate the original plaintext from the ciphertext with the correct decryption algorithm in order to read it
This pair of data transformations, encryption and decryption, together forms a cryptosystem.
There are two basic types of cryptosystems:
(i) symmetric systems, where both parties use the same (secret) key in encryption and decryption transformations;
(ii) public-key systems, also known as asymmetric systems, where the parties use two related keys, one of which is secret and the other one can be publicly disclosed.
Kerckhoffs’ Principle states that a cryptosystem should remain secure even if everything about it other than the key is public knowledge.
Symmetric Cryptography : The Advanced Encryption Standard has a fixed block size of 128 bits and a key size of 128, 192, and 256 bits.
example: RSA
In the RSA system, the receiver does as follows:
Randomly select two large prime numbers p and q, which always must be kept secret.
Select an integer number E, known as the public exponent, such that (p - 1) and E have no common divisors, and (q - 1) and E have no common divisors.
Determine the product n = p*q, known as public modulus.
Determine the private exponent, D, such that (E*D-1) is exactly divisible by both (p - 1) and (q - 1). In other words, given E, we choose D such that the integer remainder when E*D is divided by (p - 1)*(q - 1) is 1.
Release publicly the public key, which is the pair of numbers n and E, K(+) = (n, E).