之前没有设计模式基本忘完了, 再刷一遍
编写软件过程中, 程序员面临着来自 耦合性, 内聚性以及可维护性, 可扩展性, 重用性, 灵活性等多方面的挑战, 设计模式是为了让程序(软件), 具有更好的
金句:
设计模式包含了面向对象的精髓, "懂了设计模式, 你就懂了面向对象分析和设计(OOA/D)的精要"
Scott Mayers 在其巨著<<Effective C++>> 就曾经说过: C++老手和新手的区别就是前者手背上有很多伤疤
设计模式原则, 其实就是程序员在编程时, 应当遵守的原则, 也是各种设计模式的基础(即: 设计模式为什么这样设计的依据)
对于类来说, 即一个类只负责一项职责, 如类A负责两个不同的职责: 职责1, 职责2, 当职责1需求发生变更而改变A时, 可能造成职责2执行错误, 所以需要将类A的粒度拆解为 A1和A2, 分别负责职责1和职责2,这样需求变更的时候,修改A1, 而不会影响到A2
提供交通工具的运行功能
package com.dance.design.principles;
/**
* 单一职责原则
*/
public class SingleResponsibilityPrinciple {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("模特车");
vehicle.run("汽车");
vehicle.run("飞机");
}
}
// 交通工具类
class Vehicle{
public void run(String vehicle){
System.out.println(vehicle + " 在公路上运行");
}
}
可以看见, 这个类违反了单一职责原则,他不止维护了陆地上的,还维护了天上飞的,当需求变更的时候,比如飞机开始在公路上跑,后面改成了天上飞,此时就需要修改了,但是修改这个方法还会影响摩托车和汽车的功能,所以不利于维护和职责分离
创建多个类,分开维护
package com.dance.design.principles;
/**
* 单一职责原则
*/
public class SingleResponsibilityPrinciple2 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("模特车");
roadVehicle.run("汽车");
AirVehicle airVehicle = new AirVehicle();
airVehicle.run("飞机");
}
}
// 交通工具类
class RoadVehicle{
public void run(String vehicle){
System.out.println(vehicle + " 在公路上运行");
}
}
// 交通工具类
class AirVehicle{
public void run(String vehicle){
System.out.println(vehicle + " 在天上飞");
}
}
这样虽然遵守了单一职责原则, 但是改动量,比较大,而且不容易看出二者之间的业务关系,对于后续开发来说,还要参考之前的, 不利于维护,并且对代码的入侵会很大,需要具体的了解实现类
与其创建多个类,不如直接扩展方法
package com.dance.design.principles;
/**
* 单一职责原则
*/
public class SingleResponsibilityPrinciple3 {
public static void main(String[] args) {
Vehicle2 vehicle = new Vehicle2();
vehicle.run("模特车");
vehicle.run("汽车");
vehicle.runAir("飞机");
}
}
// 交通工具类
class Vehicle2{
public void run(String vehicle){
System.out.println(vehicle + " 在公路上运行");
}
public void runAir(String vehicle){
System.out.println(vehicle + " 在天上飞");
}
}
这种方式,没有对类做很大的修改,只是扩展方法,并且可以看出二者的关系,这种做法虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上还是遵守的
虽然老师讲的就到这里了,但是我还是想改一下,就是将Vehicle抽象化成抽象类,然后通过实现去完成不同的功能
package com.dance.design.principles;
/**
* 单一职责原则
*/
public class SingleResponsibilityPrinciple4 {
public static void main(String[] args) {
Vehicle3 vehicle = new RoadVehicle2();
vehicle.run("模特车");
vehicle.run("汽车");
vehicle = new AirVehicle2();
vehicle.run("飞机");
}
}
abstract class Vehicle3{
protected abstract void run(String vehicle);
}
// 交通工具类
class RoadVehicle2 extends Vehicle3{
@Override
public void run(String vehicle){
System.out.println(vehicle + " 在公路上运行");
}
}
// 交通工具类
class AirVehicle2 extends Vehicle3{
@Override
public void run(String vehicle){
System.out.println(vehicle + " 在天上飞");
}
}
这样的话, 类的职责完全分开了,并且通过继承可以看出二者之间的关系,在后续扩展的时候,可以通过继承抽象类来扩展, 并且在具体使用的时候完全可以用抽象类, new具体的实现类, 对之前的业务修改也不会很大, 比如后面添加在水里游的,直接继承抽象类即可
客户端不因该依赖它不需要的接口, 即一个类对另一个类的依赖应该建立在最小的接口上
图解
问题描述:
引入接口隔离原则:
类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D
package com.dance.design.principles.interfaceGL;
/**
* 接口隔离原则
*/
public class InterfaceIsolationPrinciple {
public static void main(String[] args) {
}
}
/**
* 接口
*/
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1 {
@Override
public void operation1() {
System.out.println("B 实现了 op1");
}
@Override
public void operation2() {
System.out.println("B 实现了 op2");
}
@Override
public void operation3() {
System.out.println("B 实现了 op3");
}
@Override
public void operation4() {
System.out.println("B 实现了 op4");
}
@Override
public void operation5() {
System.out.println("B 实现了 op5");
}
}
class D implements Interface1 {
@Override
public void operation1() {
System.out.println("D 实现了 op1");
}
@Override
public void operation2() {
System.out.println("D 实现了 op2");
}
@Override
public void operation3() {
System.out.println("D 实现了 op3");
}
@Override
public void operation4() {
System.out.println("D 实现了 op4");
}
@Override
public void operation5() {
System.out.println("D 实现了 op5");
}
}
/**
* A类依赖于 接口的 1 2 3 方法
*/
class A {
public void dp1(Interface1 i){
i.operation1();
}
public void dp2(Interface1 i){
i.operation2();
}
public void dp3(Interface1 i){
i.operation3();
}
}
/**
* C类依赖于 接口的 1 4 5 方法
*/
class C {
public void dp1(Interface1 i){
i.operation1();
}
public void dp4(Interface1 i){
i.operation4();
}
public void dp5(Interface1 i){
i.operation5();
}
}
可看出这样是存在很多问题的,类A通过Interface1依赖类B, 类C通过接口依赖类D, 如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现它们不需要的方法
将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系,也就是采用接口隔离原则
按照接口隔离原则拆分
我感觉老师的这个模型还是有点复杂,而且有改进的空间,我对此模型进行了优化
通过接口3和4直接继承2,这样就不需要去维护和2之间的关系了, A还是直接对3,B实现3, C直接对4,D实现4
package com.dance.design.principles.interfaceGL;
/**
* 接口隔离原则
*/
public class InterfaceIsolationPrinciple2 {
public static void main(String[] args) {
A1 a1 = new A1();
B1 b1 = new B1();
a1.dp1(b1);
a1.dp2(b1);
a1.dp3(b1);
C1 c1 = new C1();
D1 d1 = new D1();
c1.dp1(d1);
c1.dp4(d1);
c1.dp5(d1);
}
}
interface Interface2 {
void operation1();
}
interface Interface3 extends Interface2 {
void operation2();
void operation3();
}
interface Interface4 extends Interface2 {
void operation4();
void operation5();
}
class B1 implements Interface3 {
@Override
public void operation1() {
System.out.println("B 实现了 op1");
}
@Override
public void operation2() {
System.out.println("B 实现了 op2");
}
@Override
public void operation3() {
System.out.println("B 实现了 op3");
}
}
class D1 implements Interface4 {
@Override
public void operation1() {
System.out.println("D 实现了 op1");
}
@Override
public void operation4() {
System.out.println("D 实现了 op4");
}
@Override
public void operation5() {
System.out.println("D 实现了 op5");
}
}
/**
* A类依赖于 接口的 1 2 3 方法
*/
class A1 {
public void dp1(Interface3 i) {
i.operation1();
}
public void dp2(Interface3 i) {
i.operation2();
}
public void dp3(Interface3 i) {
i.operation3();
}
}
/**
* C类依赖于 接口的 1 4 5 方法
*/
class C1 {
public void dp1(Interface4 i) {
i.operation1();
}
public void dp4(Interface4 i) {
i.operation4();
}
public void dp5(Interface4 i) {
i.operation5();
}
}
依赖倒转原则
完成Person接收消息的功能
package com.dance.design.principles.inversion;
public class DependenceInversionPrinciple {
public static void main(String[] args) {
new Person().receive(new Email());
}
}
class Email {
public String getInfo(){
return "电子邮件信息: Hello world";
}
}
class Person{
// 消息接收
public void receive(Email email){
System.out.println(email.getInfo());
}
}
这样基本就实现了, 但是存在一些问题, 后续需求改造,不利于扩展
使用依赖倒转原则改造
原来我们是直接依赖细节的,也就是具体实现,而依赖倒转原则提倡的是依赖于抽象,所以, 我们把消息抽出一个抽象层或者功能接口,让Person去依赖
我个人偏向于抽象成功能接口的,因为接收消息,本身属于功能
让他们通过抽象去管理,而不是通过细节
package com.dance.design.principles.inversion;
public class DependenceInversionPrinciple2 {
public static void main(String[] args) {
new Person2().receive(new Email2());
new Person2().receive(new Wechat2());
}
}
interface Message {
String getInfo();
}
class Email2 implements Message {
@Override
public String getInfo() {
return "电子邮件信息: Hello world";
}
}
class Wechat2 implements Message {
@Override
public String getInfo() {
return "微信: Hello world";
}
}
class Person2 {
// 消息接收
public void receive(Message message) {
System.out.println(message.getInfo());
}
}
如果后续再次新增需求,扩展了短信或者QQ,都可以通过实现Message类来实现
package com.dance.design.principles.inversion;
/**
* 通过接口传递依赖
*/
public class DepGx {
public static void main(String[] args) {
Tv tv = new Tv();
XiaoMi xiaoMi = new XiaoMi();
tv.open(xiaoMi);
tv.close(xiaoMi);
}
}
/**
* 开关接口
*/
interface IOpenAndClose {
void open(ITV itv);
void close(ITV itv);
}
/**
* 电视接口
*/
interface ITV{
void open();
void close();
}
/**
* 小米实现
*/
class XiaoMi implements ITV{
@Override
public void open() {
System.out.println("小米电视 open...");
}
@Override
public void close() {
System.out.println("小米电视 close...");
}
}
/**
* 电视实现开关功能
*/
class Tv implements IOpenAndClose{
@Override
public void open(ITV itv) {
itv.open();
}
@Override
public void close(ITV itv) {
itv.close();
}
}
通过接口传递具体的实现
基于接口传递改造
虽然现在面向的是接口,但是接口的高度却不够, 应为现在是ITV直到电视的高度,我现在想扩展冰箱,就不足了,而且冰箱也有开关的功能,所以进行模型优化
我在电视和冰箱的上层抽离出了家电,然后让开关的接口直接依赖于家电接口,这样就可以通用冰箱和电视了,按道理来说,突然感觉抽象成接口直接让IOpenAndClose去面对家电功能接口又不太好,应该抽象出来一个抽象类(家电)去实现这个接口,然后让小米TV和海尔冰箱去继承这个抽象类的,在下面的Setter方式传递的时候再优化
package com.dance.design.principles.inversion;
/**
* 通过接口传递依赖
*/
public class DepGx {
public static void main(String[] args) {
XiaoMiTv xiaoMiTv = new XiaoMiTv();
xiaoMiTv.open(new XiaoMi());
xiaoMiTv.close(new XiaoMi());
HaiErBx haiErBx = new HaiErBx();
haiErBx.open(new HaiEr());
haiErBx.close(new HaiEr());
}
}
/**
* 开关接口
*/
interface IOpenAndClose {
void open(HouseholdElectricalAppliances householdElectricalAppliances);
void close(HouseholdElectricalAppliances householdElectricalAppliances);
}
/**
* 家电功能接口
*/
interface HouseholdElectricalAppliances {
void open();
void close();
}
/**
* 电视接口
*/
interface ITV extends HouseholdElectricalAppliances{
}
/**
* 冰箱接口
*/
interface IRefrigerator extends HouseholdElectricalAppliances{
}
/**
* 小米遥控器
*/
class XiaoMi implements ITV{
@Override
public void open() {
System.out.println("小米电视 open...");
}
@Override
public void close() {
System.out.println("小米电视 close...");
}
}
/**
* 小米电视
*/
class XiaoMiTv implements IOpenAndClose{
@Override
public void open(HouseholdElectricalAppliances itv) {
itv.open();
}
@Override
public void close(HouseholdElectricalAppliances itv) {
itv.close();
}
}
/**
* 海尔冰箱遥控器
*/
class HaiEr implements IRefrigerator {
@Override
public void open() {
System.out.println("海尔冰箱 手动打开");
}
@Override
public void close() {
System.out.println("海尔冰箱 手动关闭");
}
}
/**
* 海尔冰箱
*/
class HaiErBx implements IOpenAndClose{
@Override
public void open(HouseholdElectricalAppliances iRefrigerator) {
iRefrigerator.open();
}
@Override
public void close(HouseholdElectricalAppliances iRefrigerator) {
iRefrigerator.close();
}
}
又对上面的模型进行了优化, IOpenAndClose这个开关功能类, 并不应该面向家电抽象,应为他是一个通用的功能, 应该面向更高层的对象, 那就是泛型<T>, 谁实现,谁指定面向什么,应为开关可能是基于电视的,但是还可能是基于门的,没有任何一类可以归纳它,所以定义成泛型, 然后家电抽象类去实现这个接口,提供默认的调用实现, 然后小米电视和海尔冰箱去继承家电的抽象类,应为抽象类中提供了默认的实现,而且业务也就是简单的调用而已,所以不需要重写, 并且家电抽象类指定的泛型是家电功能接口,也就是Hou...able, 具体的打开方式由另外的功能类去实现,也就是小米Able和HaiErAble, 而家电抽象类面向的就是家电功能接口,emm这个模型比上面的要好很多
只是用来做例子而已,如果是真正开发的话,按道理来说, 家电下面应该还可以抽象出电视抽象类,冰箱抽象类,应为都是种类而不是具体的实现,所以... 开发还是要看具体的实践
package com.dance.design.principles.inversion.two;
public class DepGX2 {
public static void main(String[] args) {
HouseholdElectricalAppliances xiaomi = new XiaoMiTv();
HouseholdElectricalAppliancesAble xiaomiable = new XiaoMiAble();
xiaomi.open(xiaomiable);
xiaomi.close(xiaomiable);
HouseholdElectricalAppliances haier = new HaiErBx();
HouseholdElectricalAppliancesAble haierable = new HaiErAble();
haier.open(haierable);
haier.close(haierable);
}
}
/**
* 开关接口
*/
interface IOpenAndClose<T> {
void open(T t);
void close(T t);
}
/**
* 家电功能接口
*/
interface HouseholdElectricalAppliancesAble{
void open();
void close();
}
/**
* 家电抽象类
*/
abstract class HouseholdElectricalAppliances implements IOpenAndClose<HouseholdElectricalAppliancesAble> {
@Override
public void open(HouseholdElectricalAppliancesAble householdElectricalAppliancesAble) {
householdElectricalAppliancesAble.open();
}
@Override
public void close(HouseholdElectricalAppliancesAble householdElectricalAppliancesAble) {
householdElectricalAppliancesAble.close();
}
}
/**
* 小米遥控器
*/
class XiaoMiAble implements HouseholdElectricalAppliancesAble{
@Override
public void open() {
System.out.println("小米电视 open...");
}
@Override
public void close() {
System.out.println("小米电视 close...");
}
}
/**
* 冰箱遥控器
*/
class HaiErAble implements HouseholdElectricalAppliancesAble{
@Override
public void open() {
System.out.println("冰箱 open...");
}
@Override
public void close() {
System.out.println("冰箱 close...");
}
}
/**
* 小米电视
*/
class XiaoMiTv extends HouseholdElectricalAppliances{
}
/**
* 小米电视
*/
class HaiErBx extends HouseholdElectricalAppliances{
}
额,好像忘记改成Setter入参了,好吧,基于这个改造一下
因为模型设计的很好,所以扩展起来也很方便,因为都要改成Setter的直接在家电抽象类中改造就好,不必每个实现类都改
/**
* 家电抽象类
*/
abstract class HouseholdElectricalAppliances implements IOpenAndClose<HouseholdElectricalAppliancesAble> {
/**
* 功能接口
*/
protected HouseholdElectricalAppliancesAble householdElectricalAppliancesAble;
@Override
public void open(HouseholdElectricalAppliancesAble householdElectricalAppliancesAble) {
householdElectricalAppliancesAble.open();
}
@Override
public void close(HouseholdElectricalAppliancesAble householdElectricalAppliancesAble) {
householdElectricalAppliancesAble.close();
}
public void setHouseholdElectricalAppliancesAble(HouseholdElectricalAppliancesAble householdElectricalAppliancesAble){
this.householdElectricalAppliancesAble = householdElectricalAppliancesAble;
}
public void open(){
open(householdElectricalAppliancesAble);
}
public void close(){
close(householdElectricalAppliancesAble);
}
}
提供一个成员变量,然后提供Set方法,并且重载open和close方法
public class DepGX2 {
public static void main(String[] args) {
HouseholdElectricalAppliances xiaomi = new XiaoMiTv();
xiaomi.setHouseholdElectricalAppliancesAble(new XiaoMiAble());
xiaomi.open();
xiaomi.close();
HouseholdElectricalAppliances haier = new HaiErBx();
haier.setHouseholdElectricalAppliancesAble(new HaiErAble());
haier.open();
haier.close();
}
}
调用也很方便,okk了
package com.dance.design.principles.internal;
/**
* 里式替换原则
*/
public class InternalSubstitutionPrinciple {
public static void main(String[] args) {
System.out.println(new B().fun1(11, 3));// 本意是求11-3
System.out.println(new B().fun2(11, 3));// 和 + 9
}
}
/**
* 提供减法功能
*/
class A {
public int fun1(int a, int b) {
return a - b;
}
}
/**
* 扩展自A 实现加法
*/
class B extends A {
// 无意识重写了A的方法
@Override
public int fun1(int a, int b) {
return a + b;
}
public int fun2(int a, int b) {
return fun1(a, b) + 9;
}
}
这个案例本意是emm这我如何说呢,基本一看就看出来了,但是如果项目比较复杂, 那就不一定了
通用的做法就是: 原来的父类和子类都继承一个更通俗的基类,原有的继承去掉,采用依赖, 组合, 聚合等关系代替
改进方案
package com.dance.design.principles.internal;
/**
* 里式替换原则
*/
public class InternalSubstitutionPrinciple {
public static void main(String[] args) {
System.out.println(new B().fun1(11, 3));// 11 + 3
System.out.println(new B().fun2(11, 3));// 和 + 9
System.out.println(new B().fun3(11, 3));// 11 - 3
}
}
class Base{
}
/**
* 提供减法功能
*/
class A extends Base{
public int fun1(int a, int b) {
return a - b;
}
}
/**
* 扩展自A 实现加法
*/
class B extends Base {
/**
* 采用依赖的关系来扩展
*/
private A a = new A();
// 无意识重写了A的方法
public int fun1(int a, int b) {
return a + b;
}
public int fun2(int a, int b) {
return fun1(a, b) + 9;
}
public int fun3(int a,int b){
return this.a.fun1(a,b);
}
}
采用依赖代替继承
画图形的功能
package com.dance.design.principles.ocp;
/**
* 开闭原则
*/
public class OpenClosePrinciples {
public static void main(String[] args) {
Ge ge = new Ge();
ge.ds(new R());
ge.ds(new C());
ge.ds(new T());
}
}
/**
* 绘图的类[使用方]
*/
class Ge{
/**
* 接收SP对象,根据SP绘制不同的图像
*/
public void ds(Sp sp){
if (sp.m_type == 1){
dr(sp);
}else if(sp.m_type == 2){
dc(sp);
}else if(sp.m_type == 3){
dt(sp);
}
}
private void dr(Sp sp){
System.out.println("绘制矩形");
}
private void dc(Sp sp){
System.out.println("绘制圆形");
}
private void dt(Sp sp){
System.out.println("绘制三角形");
}
}
/**
* 基类 用于类型判断
*/
class Sp{
int m_type;
}
/**
* 矩形
*/
class R extends Sp{
R(){
m_type = 1;
}
}
/**
* 圆形
*/
class C extends Sp{
C(){
m_type = 2;
}
}
/**
* 三角形
*/
class T extends Sp{
T(){
m_type = 3;
}
}
使用开闭原则改进,这里具体的绘制,显然不应该交给Ge去实现,应为他应该只是提供功能的绘制,而不是细节,并且在扩展的时候还需要去修改
package com.dance.design.principles.ocp;
/**
* 开闭原则
*/
public class OpenClosePrinciples {
public static void main(String[] args) {
Ge ge = new Ge();
ge.ds(new R());
ge.ds(new C());
ge.ds(new T());
}
}
/**
* 绘图的类[使用方]
*/
class Ge{
/**
* 接收SP对象,根据SP绘制不同的图像
*/
public void ds(Sp sp){
sp.ds();
}
}
/**
* 基类 用于类型判断
*/
abstract class Sp{
int m_type;
abstract void ds();
}
/**
* 矩形
*/
class R extends Sp{
R(){
m_type = 1;
}
public void ds(){
System.out.println("绘制矩形");
}
}
/**
* 圆形
*/
class C extends Sp{
C(){
m_type = 2;
}
public void ds(){
System.out.println("绘制圆形");
}
}
/**
* 三角形
*/
class T extends Sp{
T(){
m_type = 3;
}
public void ds(){
System.out.println("绘制三角形");
}
}
这样的话如果还要扩展其他类型,只需要直接继承Sp类,实现ds方法即可, 不需要对其他的地方进行修改
一个学校,下属有各个学院和总部,要求打印出总部员工ID和学院员工ID
package com.dance.design.principles.demeter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 迪米特法则
*/
public class Demeter1 {
public static void main(String[] args) {
ScManage scManage = new ScManage();
scManage.printAll(new CepManage());
}
}
/**
* 学校总部员工
*/
class Emp{
public int id;
public Emp(int id) {
this.id = id;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
'}';
}
}
/**
* 学院员工
*/
class Cep{
public int id;
public Cep(int id) {
this.id = id;
}
@Override
public String toString() {
return "Cep{" +
"id=" + id +
'}';
}
}
/**
* 学院员工管理类
*/
class CepManage{
/**
* 返回学院的员工
*/
public List<Cep> getAllCep(){
return IntStream.range(0,10).mapToObj(Cep::new).collect(Collectors.toList());
}
}
/**
* 学校管理类
*/
class ScManage{
/**
* 返回学校总部的员工
*/
public List<Emp> getAllEmp(){
return IntStream.range(0,5).mapToObj(Emp::new).collect(Collectors.toList());
}
void printAll(CepManage cepManage){
System.out.println("学院员工--------");
cepManage.getAllCep().forEach(System.out::println);
System.out.println("学校员工--------");
getAllEmp().forEach(System.out::println);
}
}
设计问题:
CepManage中应该自身提供打印输出的方法,将逻辑封装到自身内部,而不是由外部去实现
CepManage中增加方法
public void printAll(){
getAllCep().forEach(System.out::println);
}
ScManage中调用即可
void printAll(CepManage cepManage){
System.out.println("学院员工--------");
cepManage.printAll();
System.out.println("学校员工--------");
getAllEmp().forEach(System.out::println);
}
原则是尽量使用合成/聚合的方式, 而不是使用继承