定义:(Bridge Pattern)
将抽象和实现解耦,使得两者可以独立地变化。
类图:
启示:
一个产品的研发,流程主要包括需求分析、产品设计、制定计划、任务安排、进度把控、产品发布、后期运维。
按照我们一般的设计思路,我们可能会如下设计:
定义一个抽象的ProjectManager类,用来定义项目的开发流程,不同的项目通过继承来实现具体的流程。每增加新的项目,就只需要继承该类重新实现即可。但这仅适用于某个单一角色对产品的管理。
我们知道一个IT公司的组织架构一般由CEO、产品经理、项目经理和员工组成。CEO负责整个公司运行项目的整体把控,产品经理负责公司的某一条产品线,项目经理则负责产品线中的某个项目的开发管理,而员工就是负责项目的开发工作。
作为一名项目经理,职责可能主要是制定开发计划和任务分配,并把控进度。但若作为一名产品经理,职责可能就需要额外负责需求分析、产品设计的工作,职位越高,责任越大嘛。
按照之前的设计,我们就需要针对同一个项目不同的角色分别提供不同的实现,这显然是不合理的。且每个角色都被赋予了同样的流程管理功能,这也是不符合现实场景的。那如何做到项目管理根据不同角色和不同项目灵活变化呢?要想做到这一点,我们就必须将之前项目管理和项目的紧耦合关系松松绑。而如何松绑呢,咱们借助『桥接模式』。
Talk is cheap, show you the code!
代码:
从面向对象的角度,我们可以抽象出经理和项目两个对象,将这些工作流程抽象为具体的行为方法。
那这些行为方法的附属该如何设置呢?经理作为项目的主要把控者,以上的工作是经理的职责所在。但是,这些工作又都是基于项目的,也就是说我们也可以将这些工作看成是项目本身的行为。你可能会按以人为本的思想,将这些行为方法定义到经理的角色里。但这样就会有一个问题,假设项目经理在项目尚未结束的时候因为某些因素辞职了或人事变动调离了,那之前的项目的工作安排就不可控了,这是不允许的。所以最好还是把这些行为方法定义到具体的项目中去。
public abstract class Project
{
public string ProjectName { get; set; }
protected Project(string projectName)
{
ProjectName = projectName;
}
/// <summary>
/// 需求分析
/// </summary>
public abstract void AnalyzeRequirement();
/// <summary>
/// 产品设计
/// </summary>
public abstract void DesignProduct();
/// <summary>
/// 制定计划
/// </summary>
public abstract void MakePlan();
/// <summary>
/// 任务分解
/// </summary>
public abstract void ScheduleTask();
/// <summary>
/// 进度把控
/// </summary>
public abstract void ControlProcess();
/// <summary>
/// 产品发布
/// </summary>
public abstract void ReleaseProduct();
/// <summary>
/// 后期运维
/// </summary>
public abstract void MaintainProduct();
}
那如何做到不同的角色赋予不同的项目管理职责呢?我们可以将职责最少的角色抽象出来。比如我们可以将项目经理制定产品计划、任务分配和进度把控抽离出来。
public abstract class Manager
{
protected Project CurrentProject { get; }
protected Manager(Project currentProject)
{
CurrentProject = currentProject;
}
/// <summary>
/// 制定计划
/// </summary>
public abstract void SchedulePlan();
/// <summary>
/// 任务分配
/// </summary>
public abstract void AssignTasks();
/// <summary>
/// 进度把控
/// </summary>
public abstract void ControlProcess();
/// <summary>
/// 项目管理
/// </summary>
public virtual void ManageProject()
{
SchedulePlan();
AssignTasks();
ControlProcess();
}
}
然后项目经理和产品经理继承实现基本职责,根据需要动态添加额外的职责。
我们看看项目经理的具体实现:
/// <summary>
/// 项目经理
/// </summary>
public class ProjectManager : Manager
{
public ProjectManager(Project currentProject) : base(currentProject)
{
}
public override void SchedulePlan()
{
base.CurrentProject.MakePlan();
}
public override void AssignTasks()
{
base.CurrentProject.ScheduleTask();
}
public override void ControlProcess()
{
base.CurrentProject.ControlProcess();
}
public override void ManageProject()
{
Console.WriteLine($"项目经理负责【{base.CurrentProject.ProjectName}】:");
base.ManageProject();
}
}
再看看产品经理的实现:
/// <summary>
/// 产品经理
/// </summary>
public class ProductManger : Manager
{
public ProductManger(Project currentProject) : base(currentProject)
{
}
public override void SchedulePlan()
{
base.CurrentProject.MakePlan();
}
public override void AssignTasks()
{
base.CurrentProject.ScheduleTask();
}
public override void ControlProcess()
{
base.CurrentProject.ControlProcess();
}
public void AnalyseRequirement()
{
base.CurrentProject.AnalyzeRequirement();
}
public void DesignProduct()
{
base.CurrentProject.DesignProduct();
}
public override void ManageProject()
{
AnalyseRequirement();
DesignProduct();
base.ManageProject();
}
public override void ManageProject()
{
Console.WriteLine($"产品经理负责【{base.CurrentProject.ProjectName}】:");
AnalyseRequirement();
DesignProduct();
base.ManageProject();
}
}
可以看到我们为产品经理额外添加了需求分析和产品设计的职责。
下面我们看看最终的场景类:
Project webProject = new WebProject("Web项目");
Manager projectManager = new ProjectManager(webProject);
Manager productManager = new ProductManger(webProject);
projectManager.ManageProject();
productManager.ManageProject();
Console.ReadLine();
总结:
通过上面的demo,我们解决了之前通过继承设计的弊端,即项目管理和项目不能灵活适应变化。通过引入桥接模式,将项目管理和项目解耦,我们实现了项目管理(抽象)和项目(实现)的分离。桥接模式的意图还是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。而这也正是桥接模式的精髓所在。
优缺点:
桥接模式将抽象和实现分离,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上;抽象和实现均可自由扩展。
应用场景:
- 不希望或不适用使用继承的场景
- 接口或抽象类不稳定的场景
- 重用性要求较高的场景
- 解决多层继承
实际案例:
数据库:Orace、SQL Server
数据库访问技术:NHibernate、EntityFramework
每种数据库都可以用不同的数据库访问技术。