博主对UML类图、设计原则、设计模式作了总结。
设计原则与设计模式
UML类图
1. 关联关系
(1) 单向关联:由类指向该类包含的成员变量类,用实线箭头:
(2) 双向关联:两个类的成员变量类型为对方类,用实线:
2. 聚合关系
是整体和部分之间的关系,即成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而存在。由部分指向整体,用实线和空心菱形:
3. 组合关系
是整体和部分之间的关系,即成员对象是整体对象的一部分,而且成员对象不可以脱离整体对象而存在。由部分指向整体,用实线和实心菱形:
4. 依赖关系
耦合度最弱,是临时的关联。可能是一个类的方法调用另一个类的方法。由前一个调用方法的类指向后一个被调用方法的类,用虚线和箭头:
5. 继承关系
子类指向父类,用实现和空心三角:
6. 实现关系
实现类指向接口,用虚线和空心三角:
设计原则
1. 开闭原则
对拓展开放,对修改关闭。
搜狗输入法增加主题或者用户选择主题时,不需要修改源代码:
2. 里氏代换原则
任何父类出现的地方,子类一定可以代替。比如如果父类的变量可以作为一个方法的参数,那么子类的变量也应可以作为该方法的参数。
这个图的正方形类继承了长方形类。但是观察resize()方法,其参数为长方形类的变量,但是如果把该参数换成正方形类的变量,该方法不能成功运行。因此违背了里氏代换原则。
所以正方形不能继承长方形,因此作出下面的修改:
3. 依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
说人话就是,尽可能用抽象进行编程,这样就降低了客户与抽象模块的耦合。
比如下面的Computer类由希捷硬盘、英特尔CPU、金士顿内存组合,违背了依赖倒转原则:
改进:对抽象进行编程,即令Computer类由抽象的接口组合:
4. 接口隔离原则
接口的实现类不应该被迫依赖于该实现类不使用的接口中的方法。因此接口中的方法应该隔离开。
如下图黑马防盗门实现了具有防盗、防火、防水的接口,但是如果增加另一个只有防盗、防火,而不实现防水的防盗门类,就不能实现这个接口了:
因此SafetyDoor接口违背了接口隔离原则。改进:
5. 组合复用原则
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如下图中使用继承关系,如果增加另一种颜色,或者增加另一种燃料驱动方式的汽车,类的种类将大大增加:
所以违背了组合复用原则。用聚合的思想修改:
6. 迪米特法则
如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
如明星不应该和粉丝、商务公司直接联系,而是聚合于经纪人,然后经纪人与粉丝、商务公司联系。
设计模式
创建型模式:
1. 工厂方法
在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
课件UML:
UML图说明:
具体产品继承抽象产品(或者具体产品实现抽象产品接口),具体工厂继承抽象工厂(或者接口);抽象工厂依赖抽象产品,具体工厂依赖具体产品。
适用场景:
(1) 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
(2) 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
缺点: 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。
2. 抽象工厂
工厂方法的升级,可实现多个风格/系列/产品族生产。
UML图说明:
具体工厂实现抽象工厂接口,具体产品实现抽象产品接口;每个抽象工厂依赖所有的抽象产品,每个具体工厂依赖所有同风格的具体产品。
UML类图:
应用场景:工厂接口的实现类实现了不同的风格/系列/产品族,保证了客户端使用的产品是相同风格的。
缺点:由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。
3. 单例
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例的运作方式:如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。
应用场景:
(1) 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
(2) 如果你需要更加严格地控制全局变量, 可以使用单例模式。
结构型模式:
4. 适配器
类适配器:目标类实现目标接口,适配器也继承目标接口,然后将接口中的方法实现为继承被适配类的方法。
UML类图:
UML类图说明:
适配器实现抽象化接口,继承被适配的类。
应用场景:
需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
5. 桥梁
如果要创建不同颜色的不同形状,用继承来实现会使类爆炸:
用桥梁模式,将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
UML类图:
使用说明:
客户端使用的是抽象角色中的方法,该角色由另一个维度聚合。在客户端分别对角色和维度进行创建,然后对角色设置维度即可。即:相当于两地分别建设,然后用一个桥将两地连接。
应用场景:
拆分或重组一个具有多重功能的庞杂类。
6. 装饰者
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
如主体为饮料,用调料来装置饮料:用调料继承饮料,因为调料的描述应该是饮料+调料;用饮料聚合到调料,因为饮料的价格是被装饰之后的价格的一部分。
应用场景:
你希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为;或者用继承来扩展对象行为的方案难以实现或者根本不可行,你可以使用该模式。
7. 观察者
观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
观察者模式 = 订阅 + 广播。类比:邮局报刊订阅。
订阅者为观察者(Observer),广播者(被观察的对象)为目标(Subject)。
UML类图:
UML类图说明:
抽象目标类中包含注册和注销观察者、同时所有观察者的方法,具体目标类继承抽象目标类并实现通知所有观察者的方法;观察者抽象接口聚合到抽象目标类,具体观察者类实现抽象观察者接口。
应用场景:
当一个对象状态的改变需要改变其他对象,或者当应用中的一些对象必须观察其他对象时,使用观察者模式。
8. 责任链
将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。避免了将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。
责任链从低级指向高级,如果低级不能处理请求,则沿着责任链让更高级处理请求。
UML类图:
UML类图说明:
Handler抽象类自聚合,维持对责任链下家的引用,声名处理请求的抽象方法;具体Handler继承抽象Handler并实现处理请求的方法。
应用场景:
有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定
在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
9. 策略
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
UML类图说明:
策略接口声名抽象策略方法,具体策略类实现策略接口中的策略方法;策略接口聚合到客户类,使客户对象可以调用策略方法。
缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
设计模式的代码实现
1. 工厂方法
//日志记录器接口:抽象产品
interface Logger {
public void writeLog();
}
//数据库日志记录器:具体产品
class DatabaseLogger implements Logger {
public void writeLog() {
System.out.println("数据库日志记录");
}
}
//文件日志记录器:具体产品
class FileLogger implements Logger {
public void writeLog() {
System.out.println("文件日志记录。");
}
}
//日志记录器工厂接口:抽象工厂
interface LoggerFactory {
public Logger createLogger();
}
//数据库日志记录器工厂类:具体工厂
class DatabaseLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//连接数据库,代码省略 ;创建数据库日志记录器对象
Logger logger = new DatabaseLogger();
//初始化数据库日志记录器代码省略
return logger;
}
}
//文件日志记录器工厂类:具体工厂
class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
//创建文件日志记录器对象
Logger logger = new FileLogger();
//创建文件,代码省略
return logger;
}
}
//客户端
class Client {
public static void main(String args[]) {
LoggerFactory factory;
Logger logger;
//可引入配置文件实现
factory = new FileLoggerFactory();
logger = factory.createLogger();
logger.writeLog();
}
}
2. 抽象工厂
//抽象工厂的接口,声明创建抽象产品对象的操作
public interface Factory {
//示例方法,创建抽象产品A的对象,return 抽象产品A的对象
public ProductA createProductA();
//示例方法,创建抽象产品B的对象,return 抽象产品B的对象
public ProductB createProductB();
}
//抽象产品A的接口
public interface ProductA {
//定义抽象产品A相关的操作
}
//产品A的具体实现
public class ProductA1 implements ProductA {
//实现产品A的接口中定义的操作
}
public class ProductA2 implements ProductA {
//实现产品A的接口中定义的操作
}
//ProductB系列是类似的代码,省略
//具体的工厂实现对象,实现创建风格1的产品对象
public class Factory1 implements Factory {
public ProductA createProductA() {
return new ProductA1();
}
public ProductB createProductB() {
return new ProductB1();
}
}
//具体的工厂实现对象,实现创建风格2的产品对象
public class Factory2 implements Factory {
public ProductA createProductA() {
return new ProductA2();
}
public ProductB createProductB() {
return new ProductB2();
}
}
public class Client {
public static void main(String[] args) {
//创建抽象工厂对象
Factory af = new Factory1();
//通过抽象工厂来获取一系列的对象,如产品A和产品B
af.createProductA(); //创建风格为1的A产品
af.createProductB();
}
}
3. 单例
//处理多线程
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
//另:双重检查加锁
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
4. 适配器
// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {
public void specificRequest() {
System.out.println("被适配类具有 特殊功能...");
}
}
// 目标接口,或称为标准接口
interface Target {
public void request();
}
// 具体目标类,只提供普通功能
class ConcreteTarget implements Target {
public void request() {
System.out.println("普通类 具有 普通功能...");
}
}
// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
public void request() {
super.specificRequest();
}
}
// 测试类
public class Client {
public static void main(String[] args) {
// 使用普通功能类
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();
// 使用特殊功能类,即适配类
Target adapter = new Adapter();
adapter.request();
}
}
5. 桥梁
//抽象化角色类
abstract class AbstractCar {
protected Transmission gear;
public abstract void run();
public void setTransmission(Transmission gear) {
this.gear = gear;
}
}
//改进(修正)抽象化角色类:按品牌分,BMW牌车
class BMWCar extends AbstractCar{
public void run() {
gear.gear();
}
}
//BenZCar..等省略
//抽象变速器
abstract class Transmission{
public abstract void gear();
}
//具体变速器:手动档
class Manual extends Transmission {
public void gear() {
System.out.println("Manual transmission");
}
}
//自动档类等省略
//有了变速器和品牌两个维度各自的实现后,可以通过聚合,实现不同品牌不同变速器的车
public class BridgeClient {
public static void main(String[] args) {
Transmission manual= new Manual();
AbstractCar bmw = new BMWCar();
bmw.setTransmission(manual);
bmw.run();
Transmission manual = new Manual();
AbstractCar benz = new BenZCar();
benz.setTransmission(manual);
benz.run();
}
}
6. 装饰者
//抽象饮料类
public abstract class Beverage {
protected String description;
public String getDescription() {
return description;
}
public abstract double cost();
}
//烘焙咖啡
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
}
public double cost() {
return .99;
}
}
//低咖类略
//调料抽象类
public abstract class CondimentDecorator extends Beverage {
protected Beverage beverage;
}
//抹茶调料
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
return .20 + beverage.cost();
}
}
//客户端
public class StarbuzzCoffee {
public static void main(String args[]) {
//低咖
Beverage beverage = new Decaf();
System.out.println(beverage.getDescription()
+ " $" + beverage.cost());
//低咖加奶加抹茶
Beverage beverage1 = new Decaf();
beverage1 = new Milk(beverage1);
beverage1 = new Mocha(beverage1);
System.out.println(beverage1.getDescription()
+ " $" + beverage1.cost());
//烘焙咖啡加奶加抹茶
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Milk(beverage2);
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());
}
}
7. 观察者
import java.util.*;
//抽象目标类
public abstract class Subject {
//定义一个观察者集合用于存储所有观察者对象
protected ArrayList<Observer> observers= new ArrayList();
//注册方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer) {
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notifyAllObservers();
}
//具体目标类
public class ConcreteSubject extends Subject {
//实现通知方法
public void notifyAllObservers() {
//遍历观察者集合,调用每一个观察者的响应方法
for(Object obs:observers) {
((Observer)obs).update();
}
}
}
//抽象观察者接口
public interface Observer {
//声明响应方法
public void update();
}
//具体观察者
public class ConcreteObserver implements Observer {
//实现响应方法
public void update() {
//具体响应代码
}
}
//客户端
public class Client {
public static void main(String args[]) {
//目标对象
Subject subject = new ConcreteSubject();
//观察者对象
Observer observer = new ConcreteObserver();
//观察者订阅目标
subject.attach(observer);
//目标通知观察者
subject.notifyAllObservers();
}
}
8. 责任链
//Handler抽象类
public abstract class Handler {
//维持对下家的引用
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor=successor;
}
public abstract void handleRequest(String request);
}
//具体Handler
public class ConcreteHandler extends Handler {
public void handleRequest(String request) {
if (请求满足条件) {
//处理请求
}
else {
this.successor.handleRequest(request); //转发请求
}
}
}
//客户端
public class Client {
public static void main(String args[]) {
Handler handler1, handler2, handler3;
handler1 = new ConcreteHandlerA();
handler2 = new ConcreteHandlerB();
handler3 = new ConcreteHandlerC();
//创建职责链
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
//发送请求,请求对象通常为自定义类型
handler1.handleRequest("请求对象");
}
}
9. 策略
//抽象策略接口
public interface CashSuper {
public double acceptCash(double money);
}
//具体策略:正常收费
public class CashNormal implements CashSuper
{
public double acceptCash(double money) {
return money;
}
}
//具体策略:八折优惠
public class CashRebate implements CashSuper{
private double moneyRebate=1;
public CashRebate(double moneyRebate){
this.moneyRebate=moneyRebate; //如八折时,传入0.8
}
public double acceptCash(double money) {
return money*moneyRebate;
}
}
//客户环境
public class CashContext {
private CashSuper cs = null;
public CashContext(CashSuper cs) {
this.cs=cs;
}
public double getResult(double money) {
return cs.acceptCash(money);
}
}
//客户端
public class SalesMan {
public static void main(String[] args) {
CashContext mSalesMan;
//策略:正常收费
CashSuper cashWay = new CashNormal();
mSalesMan = new CashContext(cashWay);
double normalResult = mSalesMan.getResult(1000);
System.out.println("平常:" + normalResult);
//策略:八折优惠
cashWay = new CashRebate(0.8);
mSalesMan = new CashContext(cashWay);
double rebateResult = mSalesMan.getResult(1000);
System.out.println("打折:" + rebateResult);
}
}
分享结束,大家辛苦了。散会!