Java框架基础-设计模式


一、设计模式介绍

1.1 设计模式概念

概念:
软件设计模式(Software Design Patten),又称设计模式,是一套被反复使用,多数人只晓的,经过分类编目的,代码设计经验的总结.它描述了在软件设计过程中一些不断重复的问题,以及该问题的解决方案.也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用.其目的是为了提高代码的复用性,代码可读性以及代码的可靠性.

优点:
1)可以提高程序员的思维能力、编程能力和设计能力。
2)使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
3)使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

核心原则:
1)找出应用可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
2)针对接口编程,而不是针对实现编程
3)为了交互对象之间的松耦合设计而努力

1.2 设计模式7大原则

单一职责原则

定义:职责是指类变化的原因,如果一个类承担了太多的职责,那么类应该被拆分.如果类承担太多职责,那么存在以下两个缺点
	1)一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力
	2)当客户需要对象的某一个职责时,不得不将其他不需要的职责都包含进来,从而造成冗余代码或者代码的浪费
	
优点:单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点.
	1)降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多
	2)提高类的可读性,复杂性降低,自然其可读性会提高
	3)提高系统可维护性,可读性提高,那就更容易维护
	4)变更引起的风险降低,变更是必然的,如果单一职责遵守的号,当修改一个功能时,可以显著降低其对其他功能的影响
	
实现方法:单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

接口隔离原则

定义:将臃肿庞大的接口拆分成更小和更具体的接口,让接口质保函客户感兴趣对的方法.以上两个定义的含义是:要为各个类建立他们需要的专用接口,而不呀视图建立一个很庞大的接口供所有依赖来它的类去调用.

优点:接口隔离原则是为了约束接口,降低类对接口的依赖性,遵守接口依赖的隔离
	1)将臃肿的接口分解为多个粒度小的接口,可以预防外来的变更的扩散,提高系统的灵活性和可维护性
	2)接口隔离提高了系统的内聚性,减少对外交互,降低了系统的耦合性
	3)如果接口的粒度大小定义合理,就能保证系统的稳定性.但是,如果定义过小,就会造成接口数量过多,是设计复杂化;定义过大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险
	4)使用多个专门的接口还能够体现对象的层次,因此可以通过接口的继承,实现总借口的定义
	5)能减少项目工程中的代码冗余.过大的接口里面通常防止许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码.

实现方法:在具体应用接口的隔离原则,应该根据以下几个规则来衡量
	1)接口尽量小,但是要有限度,一个接口只服务于一个子模块或者业务逻辑
	2)为依赖接口的类定制服务.只提供调用者需要的方法,屏蔽不需要方法
	3)了解环境,拒绝盲从.每个项目或者产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑
	4)提高内聚,减少对外交互,使接口用最少的方法完成最多的事情.

接口隔离原则和单一和单一职责都是为了提高累的内聚性,降低他们之间的耦合性,体现了封装的思想,但二者是不同的:
	1)单一接口原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
	2)单一职责主要是约束类,他针对于程序中的实现和细节,接口隔离原则主要是约束接口,主要是对抽象和程序整体框架的构建

依赖倒转原则

定义:高层模块不应该依赖底层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象.其核心思想就是面向接口编程,不要面向实现编程.
依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合
由于在软件设计中,细节具有多变性,而抽象则相对比较稳定,因此以抽象为基础搭建的框架要比以细节搭建的矿机要稳定的多,这里的抽象指的是接口或者抽象类,儿细节是指具体的实现类
使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何操作,把展现细节的任务交给他们的实现类去完成.

优点:
	1)依赖倒置可以降低类之间的耦合性
	2)依赖倒置原则可以提高系统的稳定性
	3)依赖倒置原则可以减少并行开发引起的风险
	4)依赖倒置可以提高代码的可读性和可维护性.

实现方法:依赖倒置原则的目的主要通过面向接口编程来降低类之间的耦合性,所以我们在实际编程中只要遵循以下4四点:
	1)每个类尽量提供接口或者抽象类,或者两者皆有
	2)变量的声明类型尽量是接口或者是抽象类
	3)任何类都不应该从具体类派生
	4)使用继承是遵循里氏替换原则

里氏替换原则

定义:继承必须确保超类所拥有的性质在子类中仍然成立.里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,其中蕴含的道理.里氏替换原则是集成复用的基础,他反映了基类与子类的关系,是对开闭原则的补充.是对开闭原则的补充,是实现抽象画的具体步骤的规范
	里氏替换原则通俗来讲就是,子类可以扩展父类的原有的功能,但是不能改变父类原有的功能.也就是说,出添加新的方法外,尽量不要重写父类的方法 

优点:
	1)里氏替换原则是实现开闭原则之一
	2)他克服了继承中重写父类造成的可复用性变差的缺点
	3)它的动作正确性的保证,及类的扩展不会给已有的系统引入新的系统错误,降低了代码出错的可能性
	4)加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性,可扩展性,降低需求变更同时引入的风险
	
实现方法:
	1)子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
	2)子类可以增加自己特有的方法
	3)当子类的方法可以重载父类的方法时,方法的前置条件是(方法的输入参数)要比父类的方法更加宽松
	4)当子类的方法实现父类的方法时,(重写/重载或者实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类的方法更加严格或相等
	通过重写父类的方法来完成新的功能写起来简单,但是整个继承体系的可复用性会比较差,特别是运用多台比较频繁时,程序运行出错的概率会非常大.如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方出现运行出错.这时其修正方法是:取消原来的继承关系,重新设计他们之间的关系.

开闭原则

定义:软件实体应当对扩展开放,对修改关闭.软件实体包括以下几个部分
	1.项目中划分出的模块 2.类与接口 3.方法

优点:开闭原则的话,软件测试只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能可以正常运行
	1)对软件测试的影响:软件遵守开闭原则的话,软件测试只需要对扩展的代码测试就可以了,因为原有的测试代码仍然可以正常运行
	2)可以提高代码的复用性:粒度越小,被复用的可能性就越大.在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性
	3)遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护

实现方法:可以通过"抽象约束,适应变化"来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对比较稳定的抽象层,而将相同的具体的实现类中.因为抽象灵活性好,适应性广,只要抽象的合理,可以基本的软件架构的稳定.当软件需要发生变化是,只需要重新派生一个实现类就可以了

迪米特原则

定义:只与你的直接朋友交谈,不跟"陌生人"说话,如果两个软件实体无须直接通信,那么就不应该直接相互调用,可以通过第三方调用.其目的是降低类之间的耦合度,提高模块的相对独立性
迪米特法则中的朋友是指:当前对象本身,当前对象的成员变量,当前对象方法参数等,这些对象同当前对象存在关联,聚合或组合关系,可以直接访问这些对象的方法

优点:
	1)降低了类之间的耦合度,提高了模块的相对独立性。
	2)由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
	过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

实现方法:
	1.从迪米特法则的定义和特点可知,它强调以下两点:
	2.从依赖者的角度来说,只依赖应该依赖的对象。
从被依赖者的角度说,只暴露应该暴露的方法。

所以,在运用迪米特法则时要注意以下 6 点。
1.在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
2.在类的结构设计上,尽量降低类成员的访问权限。
3.在类的设计上,优先考虑将一个类设置成不变类。
4.在对其他类的引用上,将引用其他对象的次数降到最低。
5.不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
6.谨慎使用序列化(Serializable)功能。

合成复用原则

定义:它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

优点:
	通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
1)继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
2)子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
3)它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
1)它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
2)新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。
3)复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

实现方法:合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

二、设计模式实现

1.工厂方法模式

按照实际业务划分,工厂模式有3种不同的实现方式,分别是简单工厂模式,工厂方法模式,抽象工厂模式

工厂模式的定义:定义一个创建产品的工厂接口,将产品对象的实际创建工作推迟到具体子类中,这满足创建型模式所要求的"创建与使用"分离的特点

优点:
1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
2)灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
3)典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替 换原则。

缺点:
1)类的个数容易过多,增加复杂度
2)增加了系统的抽象性和理解难度
3)抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决

应用场景: 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL电视工厂、海信电视工厂等。
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
客户不关心创建产品的细节,只关心产品的品牌。

//抽象产品:提供了产品的接口
interface Product {
    public void show();
}

//具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product {
    public void show() {
        System.out.println("具体产品1显示...");
    }
}

//具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product {
    public void show() {
        System.out.println("具体产品2显示...");
    }
}

//抽象工厂:提供了厂品的生成方法
interface AbstractFactory {
    public Product newProduct();
}

//具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory {
    public Product newProduct() {
        System.out.println("具体工厂1生成-->具体产品1...");
        return new ConcreteProduct1();
    }
}

//具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory {
    public Product newProduct() {
        System.out.println("具体工厂2生成-->具体产品2...");
        return new ConcreteProduct2();
    }
}

2.抽象工厂模式

定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版,工厂方法模式只生产一个等级结构的产品,而抽象工厂模式可以生产多个等级产品

使用抽象工厂模式一般需要满足一下条件: 1)系统中有多个产品族,每个具体工厂创建一组但属于不同等级结构的产品.2)系统一次只能消费某一族的产品,即同族产品一起使用

优点:
1)可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理
2)当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
3)抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

//抽象产品:动物类
public interface Animal {
    public void show();
}

//抽象产品:植物类
public interface Plant {
    public void show();
}

//抽象工厂:农场类
public interface Farm {
    public Animal productAnimal();
    public Plant productPlant();
}

//公共基类
public class Basic {

    public Basic(String name) {
        this.name = name;
    }

    public Basic() {
    }

    private  String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

//具体动物类1
public class Cattle extends Basic implements Animal {

    public Cattle(String name){
       super(name);
    }

    @Override
    public void show() {
        System.out.println("我是:"+this.getName());
    }
}

//具体动物类2
public class Horse  extends Basic implements Animal {

    public Horse(String name){
        super(name);
    }

    @Override
    public void show() {
        System.out.println("我是:"+this.getName());
    }

}

//具体植物类1
public class Vegetables extends Basic implements Plant {

    public Vegetables(String name){
        super(name);
    }

    @Override
    public void show() {
        System.out.println("我是:"+this.getName());
    }
}

//具体植物类2
public class Fruitage extends Basic implements Plant {

    public Fruitage(String name){
        super(name);
    }


    @Override
    public void show() {
        System.out.println("我是:"+this.getName());
    }
}

//具体农场类1
public class SGfarm implements Farm {

    @Override
    public Animal productAnimal() {
        Animal animal = new Cattle("牛");
        return animal;
    }

    @Override
    public Plant productPlant() {
        Plant plant=new Vegetables("蔬菜");
        return plant;
    }
}

//具体农场类2
public class SRfarm implements Farm {

    @Override
    public Animal productAnimal() {
        Animal animal = new Horse("马");
        return animal;
    }

    @Override
    public Plant productPlant() {
        Plant plant=new Fruitage("水果");
        return plant;
    }
}

//客户端
public static void main(String[] args) {
    Farm farm1=new SGfarm();
    Farm farm2=new SRfarm();

    System.out.println("农场1------>");
    Animal animal = farm1.productAnimal();
    Plant plant = farm1.productPlant();
    animal.show();
    plant.show();

    System.out.println("农场2------>");
    Animal animal1 = farm2.productAnimal();
    Plant plant1 = farm2.productPlant();
    animal.show();
    plant1.show();
}

3.代理模式

定义:由于某些原因需要给对象提供一个代理以控制对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象的中介

优点:
1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
2)代理对象可以扩展目标对象的功能
3)代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点:
1)代理模式会造成系统设计中类数量的增加
2)在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
3)增加了系统的复杂度

应用场景:
1)远程代理:这种方式通常为了隐藏目标存在不同地址的空间事实,方便客户端访问.例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间
2)虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉
3)安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
4)智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它
5)延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。

//接口
public interface ITeacherDao {
	
	void teach(); // 授课的方法
}

public class TeacherDao implements ITeacherDao {

	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println(" 老师授课中  。。。。。");
	}
}

//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
	
	private ITeacherDao target; // 目标对象,通过接口来聚合
	
	//构造器
	public TeacherDaoProxy(ITeacherDao target) {
		this.target = target;
	}

	@Override
	public void teach() {
		// TODO Auto-generated method stub
		System.out.println("开始代理  完成某些操作。。。。。 ");//方法
		target.teach();
		System.out.println("提交。。。。。");//方法
	}
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    //创建目标对象(被代理对象)
    TeacherDao teacherDao = new TeacherDao();

    //创建代理对象, 同时将被代理对象传递给代理对象
    TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);

    //通过代理对象,调用到被代理对象的方法
    //即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 
    teacherDaoProxy.teach();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值