面向对象设计原则及框架案例

用户头像
garlic
关注
发布于: 2020 年 09 月 27 日
面向对象设计原则及框架案例

面向对象设计原则



S.O.L.I.D 是由  Robert C. Martin 提炼总结出来的五个面向对象设计原则:

  • - Single-responsiblity principle

  • O - Open-closed principle

  • - Liskov substitution principle

  • I - Interface segregation principle

  • D - Dependency Inversion Principle



这些设计原则有助于我们消除代码中的坏味道,从而使用我们的代码可维护,易扩展。



单一职责原则(SRP):

一个类只专注做好一件事一个功能的实现, 只有引起他变化的一个原因,是软件设计中高内聚,低耦合在面向对象设计中的引申。

示例1



 Rectangle类:实现面积计算和图形展示功能.



图1-1



class Rectangle {
draw();
double area();
}



存在问题:



这个类违反了SRP原则, 因为他有两个职责:一个是在一个图形界面绘制矩形, 一个是计算面积。

如果仅通过这个类计算面积时,使用这个类同时引入了相关图形界面代码及依赖, 会增加相应的部署复杂度,也就是这些依赖的类也要一并部署,虽然实时根据就不需要他们,从而影响到使用这个类的应用Computational Gemetry Application 不得不做出相应的调整。

改进方法:

 

就是把绘图和计算这两个职责分别放到不同的类中, 这样绘制功能draw的改变不会面积计算area。



图1-2



class Rectangle {
draw();
}
class GeoMetric Rectangle {
double area();
}



示例2



大多数情况下还需要以组形式去考虑职责, 如Modem 这个例子: 实现拨号链接,数据通讯功能的类.



图2-1



interface Modem {
public void dail(String pno);
public void hangup();
public void send(char c);
public void recv();
}



存在问题:

违反了SRP原则, 这个接口存在多种职责, 进行网络链接的dail,hangup和数据通讯的send,recv, 放在了一起.



改进方法:



这个接口有两个职责一个是连接管理, 一个是数据通讯, 可以再将其拆分.



图2-2



interface DataChannel {
public void send(char c);
public void recv();
}
interface Connection {
public void dail(String pno);
public void hangup();
}



示例3



Employee 类处理雇员薪水和相关雇员信息存储.



图3-1



class Employee{
CalcuatePay();
Store();
}



存在问题:

  

  违反了SRP原则, 这个类包含了业务处理(薪水计算)和持久化两个职责, 业务处理一般会经常变动,持久化部分变动较少.

       

改进方法:

    

  拆分成两个类, 一个业务规则,一个持久化处理.



图3-2



class EmployeeRespository{
Store();
}
class Employee{
CalcuatePay();
}



开闭原则(OCP):

对修改关闭对扩展开放, 一个模块可以扩展,如果修改的话必定会影响之前逻辑处理。



示例1:



Button类: 通过按不同键实现拨号功能



图4-1



Dialer.java

public class Dialer {
public void enterDigit(int digit)
{
System.out.println(digit);
}
public void dial()
{
System.out.println("dial...");
}
}

Button.java

public class Button {
public final static int SEND_BUTTON = -99;

private Dialer dial;
int token;

public Button(int token, Dialer dial) {
this.token = token;
this.dial = dial;
}
void press() {
if (token >= 1 && token <=9) {
dial.enterDigit(token);
}
else if (token == SEND_BUTTON){
dial.dial();
}
else {
throw new UnsupportedOperationException( "unknown button pressed: token=" + token);
}
}

}



Phone.java

public class Phone {
private Dialer dialer;
private Button[] digitButtons;
private Button sendButton;
public Phone(){
dialer = new Dialer();
digitButtons = new Button[10];
for (int i=0; i<digitButtons.length; i++) {
digitButtons[i] = new Button(i, dialer);
}
sendButton = new Button(Button.SEND_BUTTON, dialer);
}
public static void main(String[] args) {
Phone phone = new Phone();
phone.digitButtons[9].press();
phone.digitButtons[1].press();
phone.digitButtons[1].press();
phone.sendButton.press();
}
}



存在问题:

违反了OCP原则, 不易修改, 如果增加一种Button需要对Button进行修改, Button调用Dial的enterDigit和dial, 修改Dial可能会影响Button;分支if/else 当逻辑增多时则不易维护;SEND按键被默认放置到了Button类中.



改进方法:

通过观察者模式,把Button进行按键动作是基于ButtonListener接口来进行抽象, 具体的Phone考虑具体实现,是在DigitButtonDialerAdapter和SendButtonDialerAdapter类中完成数字和发送键的相应处理 。





图4-2



Button.java

import java.util.LinkedList;

public class Button {
private LinkedList<ButtonListener> listeners;
public Button() {
listeners = new LinkedList<ButtonListener>();
}
public void addListener(ButtonListener listener) {
assert listeners != null;
listeners.add(listener);
}
public void press() {
for (ButtonListener listener: listeners) {
listener.buttonPressed();
}
}
}


ButtonListener.java

public interface ButtonListener {
void buttonPressed();
}



Phone.java

import javax.imageio.plugins.tiff.BaselineTIFFTagSet;

public class Phone {
private Dialer dialer;
private Button[] digitButtons;
private Button sendButton;

public Phone(){
dialer = new Dialer();
digitButtons = new Button[10];
for (int i=0; i<digitButtons.length; i++) {
digitButtons[i] = new Button();
final int digit = i;
ButtonListener digitButtonDialerAdapter = new ButtonListener() {
@Override
public void buttonPressed() {
dialer.enterDigit(digit);
}
};
digitButtons[i].addListener(digitButtonDialerAdapter);
}
sendButton = new Button();
ButtonListener sendButtonDialerAdapter = new ButtonListener() {
@Override
public void buttonPressed() {
dialer.dial();
}
};
sendButton.addListener(sendButtonDialerAdapter);
}
public static void main(String[] args) {
Phone phone = new Phone();
phone.digitButtons[9].press();
phone.digitButtons[1].press();
phone.digitButtons[1].press();
phone.sendButton.press();
}
}



里氏替换原则(LSP)

如果父类可以实现的子类也可以实现。子类型必须能够替换掉他的父类型。



示例1:



不符合IS-A不符关系的继承,一定不符合LSP原则, Square类的例子:正方形是特殊的矩形,长和宽相等。

图5-1



Rectangle.java

public class Rectangle {
private double itsWidth;
private double itsHeight;

public double getHeight() {
return itsHeight;
}
public void setHeight(double h) {
this.itsHeight = h;
return ;
}
public double getWidth() {
return itsHeight;
}
public void setWidth(double w) {
this.itsWidth = w;
return ;
}
public double getArea() {
return this.itsHeight * this.itsWidth;
}
}




Square.java

import jdk.jfr.Threshold;

import java.util.concurrent.SynchronousQueue;

public class Square extends Rectangle {

@Override
public void setWidth(double w) {
super.setWidth(w);
super.setHeight(w);
return ;
}

@Override
public void setHeight(double h) {
super.setHeight(h);
super.setWidth(h);
return ;
}

void check(Rectangle rec){
final double THRESHOLD = .0001;
rec.setHeight(5);
rec.setWidth(4);
assert(Math.abs((rec.getArea()) - 20)< THRESHOLD);
}

public static void main(String[] args) {
Square square = new Square();
square.setHeight(10);
System.out.println(square.getArea());
square.check(square);
}
}




存在问题:

  在未使用check函数前一切正常, 当年将Square传到check函数后就会发现, Square无法替换Rectangle, 他们对长度,宽度处理是不一样的。

Square虽然继承了Rectangle ,但是他不是 IS-A Rectangle, IS-A强调子类的行为模式要和父类一致。

契约原则角度来说, 子类的契约不能比父类的更严格。 这里的正方形满足长宽一致性所以,显然要比长方形要求的严格。



改进方法:

提取公共部分到基类。

这样再开发过程中上层抽象类是Quadrange类, 开发人员也就不会出现使用check函数,并传入Square情况。IDEA的编辑器也会做出类似的提示



check (Rectangle) in Square cannot be applied to (Square)



图5-2



Quadrangle.java

public class Quadrangle {
public double getHeight() { return 0.0;}
public double getWidth() { return 0.0;}
public double getArea() {
return this.getHeight() * this.getWidth();
}
}




Rectangle.java

public class Rectangle extends Quadrangle{
private double itsWidth;
private double itsHeight;

public void setHeight(double h) {
this.itsHeight = h;
return ;
}

public void setWidth(double w) {
this.itsWidth = w;
return ;
}

}



Square.java

import jdk.jfr.Threshold;

import java.util.concurrent.SynchronousQueue;

public class Square extends Quadrangle {
private double itsSide;

public double getSide() {
return this.itsSide;
}

public void setSide(double s) {
this.itsSide = s;
return ;
}
@Override
public double getHeight() {
return this.itsSide;
}

@Override
public double getWidth() {
return this.itsSide;
}

void check(Rectangle rec){
final double THRESHOLD = .0001;
rec.setHeight(5);
rec.setWidth(4);
assert(Math.abs((rec.getArea()) - 20)< THRESHOLD);
}

public static void main(String[] args) {
Square square = new Square();
square.setSide(10);
square.getArea();
System.out.println(square.getArea());
//square.check(square);
// check (Rectangle) in Square cannot be applied to (Square)
}
}



判断LSP原则的不只看他的设计,还要根据该设计的使用者所作出的合理假设来审视他。LSP 是使代码符合开闭原则(OCP)的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。

另外,如果可以是用组合方式来实现扩展的, 应当优先选择组合。



接口分离原则(ISP)



把功能是实现在接口中而不是类中。 多个专门的接口比一个接口好,这样我们在使用不同功能时就调用不同的接口, 不需要实现全部的功能。



示例1:



Doors可以被加锁或解锁,并且可以知道自己当前状态, TimeDoors是一类特殊Doors,如果门开的时间过长, 就发出报警。



图6-1



Door.java

public class Door implements TimerClient {
public void Lock(){
this.isOpen = false;
System.out.println("Door is Closed ");
return ;
}
public void Unlock() {
isOpen = true;
System.out.println("Door is Opened ");
return ;
}
public boolean isDoorOpen(){
if (isOpen == true){
return true;
} else {
return false;
}
}
private boolean isOpen;
public void Door(){
isOpen = false;
return ;
}

@Override
public void timeout() {
return;
}
}



TimerDoor.java

import java.time.zone.ZoneOffsetTransitionRule;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class TimerDoor extends Door {
@Override
public void timeout() {
if (this.isDoorOpen()) {
System.out.println("beep beep beep ...");
}
}

public static void main(String[] args) throws Exception {

TimerDoor timerDoors = new TimerDoor();

CheckDoor checkDoor = new CheckDoor(timerDoors);

Thread thread = new Thread(checkDoor);

thread.start();

//门打开
timerDoors.Unlock();
//8秒后关闭
TimeUnit.SECONDS.sleep(8);
timerDoors.Lock();
// 1秒中打开
TimeUnit.SECONDS.sleep(1);
timerDoors.Unlock();
// 再关闭
//timerDoors.Lock();

checkDoor.doStop();;
return;

}
}




CheckDoor.java

public class CheckDoor implements Runnable {

private TimerDoor timerDoors;

public CheckDoor(TimerDoor timerDoors) {
this.timerDoors = timerDoors;
}

private boolean doStop = false;

public synchronized void doStop() {
this.doStop = true;
}

public synchronized boolean keepRunning() {
return this.doStop == false;
}

public void run() {
System.out.println("CheckDoor start...");
while (keepRunning()) {
if (timerDoors.isDoorOpen()){
System.out.println("CheckDoor: Door is opened");
Timer t = new Timer();
try {
t.Register(10000, timerDoors);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
System.out.println("CheckDoor: Door is closed");
}

try {
Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}




Timer.java

import java.util.Date;
import java.util.TimerTask;

public class Timer {

public void Register(int timeout, TimerClient client) throws InterruptedException {
//todo 使用javatimer实现一个定时器,时间到了通知调用Timeclient
//todo 中的timeout

java.util.Timer timer = new java.util.Timer();
timer.schedule(new TimerTask() {

@Override
public void run() {
System.out.println("Task performed on: " + new Date() + "n" +
"Thread's name: " + Thread.currentThread().getName()
);
client.timeout();
timer.cancel();

}
}, timeout);
//timer.cancel();

return;
}
}



TimerClient.java

public interface TimerClient{
public void timeout();
}



输出:

Door is Opened
CheckDoor start...
CheckDoor: Door is opened
Door is Closed
Door is Opened
Task performed on: Sun Sep 27 10:47:14 CST 2020nThread's name: Timer-0
beep beep beep ...

Process finished with exit code 0




存在问题:



由于TimerDoor的需求,使得所有Door都需要实现timeout方法, 而并不是所有门都需要, 当Timer发生改变时如 增加唯一ID区分准确判断超时, (这种异常出现在,如果在门被打开后,CheckDoor检查到后向Timer发送一个消息, 可是在超时前, 关闭了一会儿又打开了)。这个修改涉及Door及所有的Door对象, 而这其中也包含不是TimerDoor其他派生的Door。这种修改代价和影响就变得不可预测。



增加TimeId涉及调整代码



TimeClient.java

public interface TimerClient{
public void timeout(int timeoutID);
}



Timer.java Register 增加了timeoutID参数, client.timeout也进行调整。

import java.util.Date;
import java.util.TimerTask;

public class Timer {

public void Register(int timeout, int timeoutID, TimerClient client) throws InterruptedException {
//todo 使用javatimer实现一个定时器,时间到了通知调用Timeclient
//todo 中的timeout

java.util.Timer timer = new java.util.Timer();
timer.schedule(new TimerTask() {

@Override
public void run() {
System.out.println("Task performed on: " + new Date() + "n" +
"Thread's name: " + Thread.currentThread().getName()
);
client.timeout(timeoutID);
timer.cancel();

}
}, timeout);
//timer.cancel();

return;
}
}




Door.java 增加了timeoutID的属性记录每个开门事件的ID

public class Door implements TimerClient {
public void Lock(){
this.isOpen = false;
System.out.println("Door is Closed ");
return ;
}
public void Unlock() {
isOpen = true;
System.out.println("Door is Opened ");
// add timeoutID
timeoutID ++;
return ;
}
public boolean isDoorOpen(){
if (isOpen == true){
return true;
} else {
return false;
}
}
private boolean isOpen;

public void Door(){
isOpen = false;
return ;
}

public int timeoutID = 0;
public int getTimeoutID(){
return timeoutID;
}

@Override
public void timeout(int timeoutID) {
return;
}
}





CheckDoor.java 适应性调整

public class CheckDoor implements Runnable {

private TimerDoor timerDoors;

public CheckDoor(TimerDoor timerDoors) {
this.timerDoors = timerDoors;
}

private boolean doStop = false;

public synchronized void doStop() {
this.doStop = true;
}

public synchronized boolean keepRunning() {
return this.doStop == false;
}

public void run() {
System.out.println("CheckDoor start...");
while (keepRunning()) {
if (timerDoors.isDoorOpen()){
System.out.println("CheckDoor: Door is opened");
Timer t = new Timer();
try {
t.Register(10000, timerDoors.getTimeoutID(), timerDoors);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
System.out.println("CheckDoor: Door is closed");
}

try {
Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}



TimerDoor.java

import java.time.zone.ZoneOffsetTransitionRule;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class TimerDoor extends Door {
@Override
public void timeout(int timeoutID) {
if (this.isDoorOpen() && timeoutID == this.timeoutID) {
System.out.println("beep beep beep ...");
}
}

public static void main(String[] args) throws Exception {

TimerDoor timerDoors = new TimerDoor();

CheckDoor checkDoor = new CheckDoor(timerDoors);

Thread thread = new Thread(checkDoor);

thread.start();

//门打开
timerDoors.Unlock();
//8秒后关闭
TimeUnit.SECONDS.sleep(8);
timerDoors.Lock();
// 1秒中打开
TimeUnit.SECONDS.sleep(1);
timerDoors.Unlock();
// 再关闭
//timerDoors.Lock();
TimeUnit.SECONDS.sleep(11);
checkDoor.doStop();;
return;

}
}



输出:可以看到增加ID后不会错误报警,但是改动了Door, 会影响其他派生类

Door is Opened
CheckDoor start...
CheckDoor: Door is opened
Door is Closed
Door is Opened
CheckDoor: Door is opened
Task performed on: Sun Sep 27 12:06:34 CST 2020nThread's name: Timer-0
Task performed on: Sun Sep 27 12:06:44 CST 2020nThread's name: Timer-1
beep beep beep ...

Process finished with exit code 0



改进方法:

使用委托分离接口, 创建一个派生自TimeClient类的对象,并把他的请求委托给TimerDoor, 当TimerDoor向Timer注册一个超时请求时就创建一个DoorTimerAdapter,并把他注册给Timer,当Timer对象发送TimeOut消息给DoorTimerAdapter, DoorTimerAdapter将消息委托给TimerDoor.



图6-2



Timer.java TimerClient 无变化

Door.java Door调整后更专注门的基本功能删除了相关Timerclient的实现

public class Door {
public void Lock(){
this.isOpen = false;
System.out.println("Door is Closed ");
return ;
}
public void Unlock() {
isOpen = true;
System.out.println("Door is Opened ");
// add timeoutID
//timeoutID ++;
return ;
}
public boolean isDoorOpen(){
if (isOpen == true){
return true;
} else {
return false;
}
}
private boolean isOpen;

public void Door(){
isOpen = false;
return ;
}

//public int timeoutID = 0;
//public int getTimeoutID(){
// return timeoutID;
//}

}





TimerDoor.java 可以实现了自己特有一些属性和处理TimeoutID,timeout



import java.time.zone.ZoneOffsetTransitionRule;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class TimerDoor extends Door {

private int timeoutID = 0;

@Override
public void Unlock() {
super.Unlock();
timeoutID ++;
return ;
}

public int getTimeoutID(){
return timeoutID;
}

public void timeout(int timeoutID){
if (this.isDoorOpen() && timeoutID == getTimeoutID()) {
System.out.println("beep beep beep ...");
return;
}
}

public static void main(String[] args) throws Exception {

TimerDoor timerDoors = new TimerDoor();

DoorTimeAdapter timerDoorsAapdter = new DoorTimeAdapter(timerDoors);

CheckDoor checkDoor = new CheckDoor(timerDoorsAapdter);

Thread thread = new Thread(checkDoor);

thread.start();

//门打开
timerDoors.Unlock();
//8秒后关闭
TimeUnit.SECONDS.sleep(8);
timerDoors.Lock();
// 1秒中打开
TimeUnit.SECONDS.sleep(1);
timerDoors.Unlock();
// 再关闭
//timerDoors.Lock();
TimeUnit.SECONDS.sleep(11);
checkDoor.doStop();;
return;

}
}




DoorTimeAdapter.java 进行适配到TimerClient.



public class DoorTimeAdapter implements TimerClient {

private TimerDoor timerDoors;

public DoorTimeAdapter(TimerDoor timerDoors){
this.timerDoors = timerDoors;
}
public int getTimeroutID(){
return timerDoors.getTimeoutID();
}
public boolean isDoorOpen(){
return timerDoors.isDoorOpen();
}

@Override
public void timeout(int timeoutID){
timerDoors.timeout(timeoutID);
}
}




CheckDoor.java 也使用 DoorTimeAdapter来进行判断



public class CheckDoor implements Runnable {

private DoorTimeAdapter timerDoorsAdapter;

public CheckDoor(DoorTimeAdapter timerDoorsAdapter) {
this.timerDoorsAdapter = timerDoorsAdapter;
}

private boolean doStop = false;

public synchronized void doStop() {
this.doStop = true;
}

public synchronized boolean keepRunning() {
return this.doStop == false;
}

public void run() {
System.out.println("CheckDoor start...");
while (keepRunning()) {
if (timerDoorsAdapter.isDoorOpen()){
System.out.println("CheckDoor: Door is opened");
Timer t = new Timer();
try {
t.Register(10000, timerDoorsAdapter.getTimeroutID(), timerDoorsAdapter);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
System.out.println("CheckDoor: Door is closed");
}

try {
Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}




这种方式 DoorTimeAdapter感觉还是比较臃肿的. 看下另外一种出方式, 多重继承



图6-3



TimerDoor.java

import java.time.zone.ZoneOffsetTransitionRule;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class TimerDoor extends Door implements TimerClient{

private int timeoutID = 0;

@Override
public void Unlock() {
super.Unlock();
timeoutID ++;
return ;
}

public int getTimeoutID(){
return timeoutID;
}
@Override
public void timeout(int timeoutID) {
if (this.isDoorOpen() && timeoutID == this.timeoutID) {
System.out.println("beep beep beep ...");
}
}

public static void main(String[] args) throws Exception {

TimerDoor timerDoors = new TimerDoor();

CheckDoor checkDoor = new CheckDoor(timerDoors);

Thread thread = new Thread(checkDoor);

thread.start();

//门打开
timerDoors.Unlock();
//8秒后关闭
TimeUnit.SECONDS.sleep(8);
timerDoors.Lock();
// 1秒中打开
TimeUnit.SECONDS.sleep(1);
timerDoors.Unlock();
// 再关闭
//timerDoors.Lock();
TimeUnit.SECONDS.sleep(11);
checkDoor.doStop();;
return;

}
}




相对于使用委托模式, 多重继承实现起来更简单,也不需要创建多余的类.



依赖倒置原则(DIP)



高层的模块不依赖底层的模块, 而是依赖高层的抽象.



如墙上的开关,他不依赖具体电灯实现, 也不用关心控制什么设备。 只依赖抽象开关的实现, 电灯和其他电器依赖抽象开关的实现, 这样就可以用开关控制所有的电器。



示例1:

开关的例子



图7-1



Button.java

public class Button {
public Lamp lamp;
void poll() {
if (lamp.isOpen()){
lamp.turnOff();
} else {
lamp.turnOn();
}
}
public static void main(String argv[]){
Button button = new Button();
button.lamp = new Lamp();
button.poll();
button.poll();
}
}




Lamp.java



public class Lamp {
private boolean isOpen;

public Lamp(){
isOpen = false;
}

public void turnOn(){
isOpen = true;
System.out.println("lamp is opened");
}

public void turnOff(){
isOpen = false;
System.out.println("lamp is closed");
}

public boolean isOpen(){
return isOpen;
}
}




存在问题:



如果这时家里添置了冰箱, 那么需要新生成一个处理冰箱Button类. 这时就可以将Button进行抽象.



改进方法:

对按键进行抽象抽出一个ISwitch开关接口. 所有设备都遵循这个接口规则进行实现



图7-2



Button.java


import javax.imageio.event.IIOWriteProgressListener;

public class Button {
public ISwitch item;

void poll() {
if (item.isOpen()){
item.turnOff();
} else {
item.turnOn();
}
}
public Button(ISwitch item) {
this.item = item;
}
public static void main(String argv[]){
Lamp lamp = new Lamp();
Drawer drawer = new Drawer();
Button buttonLA = new Button(lamp);
buttonLA.poll();
buttonLA.poll();
Button buttonDR = new Button(drawer);
buttonDR.poll();
buttonDR.poll();
}
}



ISwitch.java

public interface ISwitch {
public void turnOn();
public void turnOff();
public boolean isOpen();
}



Lamp.java

public class Lamp implements ISwitch{
private boolean isOpen;

public Lamp(){
isOpen = false;
}

public void turnOn(){
isOpen = true;
System.out.println("lamp is opened");
}

public void turnOff(){
isOpen = false;
System.out.println("lamp is closed");
}

public boolean isOpen(){
return isOpen;
}
}




增加Drawer.java,



public class Drawer implements ISwitch{
private boolean isOpen;

public Drawer(){
isOpen = false;
}

public void turnOn(){
isOpen = true;
System.out.println("Drawer is opened");
}

public void turnOff(){
isOpen = false;
System.out.println("Drawer is closed");
}

public boolean isOpen(){
return isOpen;
}
}




可以看到只要创建相关button对象即可接入不同设备, 不需要重新编写类



框架案例



反应式编程Flower框架设计

Actor模型



Actor模型是概念上的并发模型, 于1973年问世, 他定义了一些通用的准则, 知道系统组件如何在并发环境中进行交互, Actor模型, 最著名的实现是akka 和 erlang。



Actor模型的Actor是基本单元, 他完成以下功能:



  • 创建Actor

  • 发送消息

  • 指定如何处理下一条消息



图8



特点优势

  • 易扩展:模型简单,方便扩展

  • 容错:可以创建多个Actor,并通过重启或重定向消息进行修复

  • 独立性无状态:只处理消息, 无需等待响应



不足

  • 邮箱溢出

  • 死锁



Flower的框架设计



Flower核心类



Flower基于Akka的Actor进行开发,将Service封装到Actor里面,Actor收到的消息作为参数传入Service进行调用,Service的输出发送给后续Actor作为Service的输入。



图9



  • 用户开发的Service实现Service或者HttpService接口

  • ServiceFactory负责用户以及框架内置的service实例管理(加载*.services文件)

  • ServiceFlow负责流程管理(加载*.flow文件)

  • ServiceActor将Service封装到Actor



Flower初始化及调用序列



图10



服务流程初始化



  • 开发者通过ServiceFacade调用已经定义好的服务流程

  • ServiceFacade根据传入的flow名和service名,创建第一个ServiceActor

  • ServiceActor通过ServiceFactory装载Service实例,并通过ServiceFlow获得配置在流程中当前Service的后续Service(可能有多个)

  • 递归创建后续Service的ServiceActor,并记录后续ServiceActor的ActorRef



消息流处理



  • 调用者发送给ServiceFacade的消息,将被flow流程中的第一个ServiceActor处理

  • ServiceActor调用对应的Service实例

  • ServiceActor将Service实例的返回值作为消息发送给流程定义的后续ServiceActor



参考及引用



架构师训练营作业-李智慧老师相关讲义

关于Java Timer的一篇文章 https://www.baeldung.com/java-timer-and-timertask

Photo by Marko Blazevic from Pexels



用户头像

garlic

关注

还未添加个人签名 2017.11.15 加入

还未添加个人简介

评论

发布
暂无评论
面向对象设计原则及框架案例