极客大学架构师训练营 框架开发 上课总结 第五课

用户头像
John(易筋)
关注
发布于: 2020 年 06 月 25 日

说明

框架开发



讲师:李智慧

框架设计原则 (SOLID)



开闭原则(OCP - Open–closed principle)

对扩展是开发,对现有修改是关闭。

使用多态,使用抽象。策略模式,适配器模式,观察者模式。



依赖倒置原则(DIP - Dependency inversion principle)

高层不依赖低层,低层也不依赖高层。高层定义抽象接口,低层实现高层定义的接口。



比如Tomcat,Spring,JUnit,我们不需要调用框架的代码。我们基于框架提供的接口来编程。 架构师要有开发框架的能力和思维,这才能区别于普通的工程师。

正常的调用顺序

依赖倒置以后



Liskov 替换原则(LSP - Liskov substitution principle)

所有的父类,都可以替换为子类。 子类比父类更严格,子类方法约束开放性要比父类大。比如父类是protected修饰,子类只能是protected或者public。



不是为继承而设计的类,那么就不要继承该类。



单一职责原则(SRP - Single-responsibility principle)

一个类只有一个引起其变化的原因。

如果一个类会有多个原因引起变化,则要分为多个类。



接口隔离原则(ISP - Interface segregation principle)

隐藏用户不需要的接口,以免被误操作。



反应式编程框架 Flower 设计

程序是如何运行又是如何崩溃的

方法调用都是同步的,很快就会把线程池里面的线程给消费完了,导致排队阻塞。



Flower 可以显著提升系统性能

异步处理,通过消息来通信。

Flower 反应式重构前后性能对比

Flower 可以显著提升系统性能 架构图

Flower 实现异步的基础是 Akka 的 Actor

Sender 到 Actor 的通信是异步的,通过消息 Message 进行通信。

5分钟上手 Flower 反应式编程

Akka编程实例(Java)

public class Hello extends UntypedActor {
@Override
public void onReceive(Object message) {
System.out.println("Hello, world!");
}
}

调用

ActorRef hello = ...
hello.tell("Hi!");

1 开发异步服务Service

public class ServiceA implements Service<String, String> {
public String process(String message, ServiceContext context) {
return ((String) message).trim();
}
return "";
}

2. 编排Service流程 serviceA -> serviceB -> serviceC

ServiceFlow.getOrCreate(flowName, servcieFactory).buildFlow(ServiceA.class, ServiceB.class).buildFlow(ServiceB.class, ServiceC.class);



3. 调用异步服务

flowerFactory.getServiceFacade().asyncCallService(flowName, "Hello World!");



Flower 可视化流程编排

// -> Service1 -> Service2 -> Service5 -> Service4
// ^ | ^ |
// | -> Service3 -| |
// |____________________________________________|
Service1 -> Service2
Service1 -> Service3
Service2 -> Service5
Service3 -> Service5
Service5 -> Service4
Service4 -> Service1



兼容 Spring 的 Flower Web 开发

@RestController
@RequestMapping("/order/")
@Flower(value = "createOrderFlow", flowNumber = 32)
public class CreateOrderController extends FlowerController {
@RequestMapping(value = "createOrder")
public void createOrder(OrderExt orderDTO, HttpServletRequest req) throws IOException {
doProcess(orderDTO, req);
}
}



protected void doProcess(Object param, HttpServletRequest req) throws IOExeption {
AsyncContext context = null;
if (req != null) {
context = req.startAsync();
}
flowRouter.asyncCallService(param, context);
}

Flower 异步数据库访问



@FlowerService
public class UserService implements Service<User, CompletableFuture<Serializable>> {
@Autowired
private UserDao userDao;
@override
public CompletableFuture<Serializable> process(User message, ServiceContext context) throws Throwable {
System.out.println("handle user message: " + message);
return userDao.insert(message);
}
}

Flower 核心模块



publci class ServiceActor extends UntypedActor {
Service service;
Set<ActorRef> nextServiceActors;
public ServiceActor(String serviceName) throws Exception {
this.service = ServiceFactory.getService(serviceName);
nextServiceActors = new HashSet<ActorRef>();
Set<String> nextServiceNames = ServiceFlow.getNextFlow(serviceName);
if (nextServiceNames != null && !nextServiceNames.isEmpty()) {
for (String str : nextServiceNames) {
nextServiceActors.add(ServiceActorFactory.buildServiceActor(str));
}
}
}
@Override
public void onReceive(Object arg0) throws Throwable {
Object 0 = service.process(arg0);
if (nextServiceActors != null && !nextServiceActors.isEmpty()) {
for (ActorRef actor : nextServiceActors) {
actor.tell(o, getSelf());
}
}
}
}



package com.ly.train.flower.common.actor;
public class ServiceFacade {
public static void callService(String serviceName, Object o) throws Exception {
ServiceActorFactory.buildServiceActor(serviceName).tell(o, null);
}
}
public class ServiceActorFactory {
final static ActorSystem system = ActorSystem.create("LocalFlower");
private static FiniteDuration duration = Duration.create(3, SECONDS);
public static Map<String, ActorRef> map = new HashMap<String, ActorRef>();
public static ActorRef buildServiceActor(String serviceName) throws Exception {
ActorRef actor = map.get(serviceName);
if (actor != null) {
return actor;
}
actor = system.actorOf(Props,create(ServiceActor.class, serviceName));
return actor;
}
}

Flower 的分布式异步微服务解决方案

为什么选择 Flower

Flower 与 WebFlux、RxJava 的比较优势

  • 开发工程师无需学习函数式编程即可开发反应式系统

  • 纯消息驱动,可以实现更灵活的扩展(事件溯源、分布式事务、限流)。



更好的性能与更低的成本

  • 更好的性能意味着用更少的机器就可以满足系统的并发压力。



更高的可用性

  • 消息驱动天然限流特性使系统在过高并发和部分组件失效的情况下保障系统不会崩溃。



开源地址:

https://github.com/zhihuili/flower



面向对象的设计模式

设计模式的作用



设计模式的定义

什么是设计模式?

  • 每一种模式都描述了一种问题的通用解决方案。这种问题在我们的环境中,不停地出现。

  • 设计模式是一种可重复使用的解决方案。



一个设计模式的四个部分:

  • 模式的名称 - 由少量的字组成的名称,有助于我们表达我们的设计。

  • 待解问题 - 描述了何时需要运用这种模式,以及运用模式的环境(上下文)。

  • 解决方案 - 描述了组成设计的元素(类和对象)、它们的关系、职责以及合作。但这种解决方案是抽象的,它不代表具体的实现。

  • 结论 - 运用这种方案所带来的利和弊。主要是指它对系统的弹性、扩展性和可移植性的影响。



设计模式的分类

从功能分

  • 创建模式(Creational Patterns)

☞ 对类的实例化过程的抽象。

  • 结构模式(Structural Patterns)

☞ 将类或对象结合在一起形成更大的结构。

  • 行为模式(Behavioral Patterns)

☞ 对在不同的对象之间划分责任和算法的抽象化。



从方式分

  • 类模式

☞ 以继承的方式实现模式,静态的。

  • 对象模式

☞ 以组合的方式实现模式,动态的。



排序问题 -- 如何创建一个对象?



利用 简单工厂



简单工厂及 Client 程序

public class SorterFactory {
public static <T> Sorter<T> getSorter() {
return new BubbleSorter<T>();
}
}
public class Client {
public static void main() {
Integer[] array = { 5, 4, 9, 7, 6, 3, 8, 1, 0, 2};
Sorter<Integer> sorter = SorterFactory.getSorter();
Sortable<Integer> sortable = SortableFactory.getSortable(array);
Comparator<Integer> comparator = ComparatorFactory.getComparator();
sorter.sort(sortable, comparator);
......
}
}



简单工厂的优缺点

优点:

  • 使 Client 不再依赖 Sorter 的具体实现(如 BubbleSorter)

  • 对 Client 实现 OCP - 增加 Sorter 不影响 Client



缺点:

  • 对 Factory 未实现 OCP - 增加 Sorter 需要修改 Factory



对简单工厂的改进一

class SorterFactory_2 {
@SuppressWarnings("unchecked")
public static <T> Sorter<T> getSorter (String implClass) {
try {
Class impl = Class.forName(implClass);
return (Sorter<T>) impl.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Illegal class name:" + implClass, e);
}
}
}
Sorter<Integer> sorter = SorterFactory_2.getSorter("demo.sort.impl.BubbleSorter");



改进一所存在的问题

解决了 Factory 的 OCP 问题吗?

  • 增加 Sorter 实现时,不需要修改 Factory 了

  • 但是仍然需要修改 Client



其它问题

  • 丧失了编译时的类型安全。

☞ Client 和 Factory 均类型不安全。

  • Client 仍然 ”知道“ Sorter 的实现是什么。

  • 限制了 Sorter 的实现只能通过 ”默认构造函数“ 创建



对简单工厂的改进二

class SorterFactory_3 {
private final static Properties IMPLS = loadImpls();
private static Properties loadImpls() {
Properties defaultImpls = new Properties();
Properties impls = new Properties(defaultImpls);
defaultImpls.setProperty("sorter", "demo.sort.impl.BubbleSorter");
try {
impls.load(SorterFactory_3.class.getResourceAsStream("sort.properties"));
} catch ( IOException e) {
throw new RuntimeException(e);
}
return impls;
}
@SuppressWarning("unchecked")
public static <T> Sorter<T> getSorter() {
String implClassName = IMPLS.getProperty("sorter");
try {
Class implClass = Class.forName(implClassName);
return (Sorter<T>) implClass.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Illegal class name: " + implClassName, e);
}
}
}

创建 sort.proproties 文件

sorter=demo.sort.impl.BubbleSorter



改进二的优缺点

优点

  • 满足 OCP?

☞ 对 Client 和 Factory 均满足

☞ 满足 OCP 方法

☞ 抽象

☞ 动态编程(即将编译时类型检查转变成运行时检查)



缺点

  • 缺少编译时类型安全

  • 限制了 Sorter 的实现只能通过 ”默认构造函数“ 创建

☞ 假如需要传递参数?



简单工厂改进做法其实相当重要

  • 简单工厂非常重要,是许多其它模式的基础

  • 而该机制解决了简单工厂模式最致命的问题



Singleton 单例模式



问什么要试用 Singleton

Singleton 模式保证产生单一实例,就是说一个类只产生一个实例。试用 Singleton 有两个原因

  • 是因为只有一个实例,可以减少实例频繁创建和销毁带来的资源消耗。

  • 是当多个用户试用这个实例的时候,便于进行统一控制(比如打印机对象)。



前者是性能需求,后者是功能需求。



饿汉式 Singleton

public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}



注意:一定要有私有的构造函数,保证实例只能通过getInstance() 方法获得。



尽量使用饿汉式构造单实例。单例中的成员变量是多线程重用的,可能会产生意想不到的结果,因此尽量将单例设计为无状态对象(只提供服务,不保存状态)。



饿汉式 Singleton 的问题是,应用一启动就加载对象进内存,就算从来未被用过。

懒汉式 Singleton

publci class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

注意: getInstance() 的修饰符 synchronized 一定要加上,否则可能会产生多重实例。



懒汉式Singleton解决了饿汉式Singleton,当调用的时候才去加载对象到内存。但是引发了另外一个性能问题,每次访问对象都要加锁。



提升性能的 懒汉式 Singleton

publci class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = LazySingleton.buildInstance();
}
return instance;
}
private static synchronized LazySingleton buildInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}



只有当对象为null的时候,才去加锁创建对象。

适配器模式

适配器的作用:

系统需要使用现有的类,而这个类的接口与我们所需要的不同。

  • 例如: 我们需要对 List 进行排序,但是我们需要一个 Sortable 接口,原有的 List 接口不能满足要求。

类的适配器

由于原 Sortable 接口和 ArrayList 不兼容,只好定义一个 NewSortable

public class Sortable<T> extends ArrayList<T> implements NewSortable<T> {
public T getElement(int i) {
return get(i);
}
public void setElement(int i, T o) {
set(i, o);
}
}
public interface NewSortalbe<T> {
int size();
T getElement(int i);
void setElement(int i, T o);
}



对象适配器

public class ListSortable<T> implements Sortable<T> {
private final List<T> list;
public ListSortable(List<T> list) {
this.list = list;
}
public int size() {
return list.size();
}
public T get(int i) {
return list.get(i);
}
public void set(int i, T o) {
list.set(i, o);
}
}



适配器的应用

JDBC Driver

  • 是对具体数据库的适配器

  • 例如,将 Oracle 适配到 JDBC 中



JDBC-ODBC Bridge

  • 是将 Windows ODBC 适配到 JDBC 接口中



注意:以上信息如有侵权,请联系作者删除,谢谢。



发布于: 2020 年 06 月 25 日 阅读数: 43
用户头像

John(易筋)

关注

问渠那得清如许?为有源头活水来 2018.07.17 加入

工作10+年,架构师,曾经阿里巴巴资深无线开发,汇丰银行架构师/专家。开发过日活过亿的淘宝Taobao App,擅长架构、算法、数据结构、设计模式、iOS、Java Spring Boot。易筋为阿里巴巴花名。

评论

发布
暂无评论
极客大学架构师训练营 框架开发 上课总结 第五课