極客大學架構師訓練營 框架開發 上課總結 第五課

說明

框架開發

講師:李智慧

框架設計原則 (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 接口中

注意:以上信息如有侵權,請聯繫作者刪除,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章