23种设计模式
简介:
1)、设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题 的通用解决方案,设计模式(Designpatterm)代表了最佳的实践。这些解决方案是众多软件开发人员经过 相当长的一段时间的试验和错误总结出来的。
2)、设计模式的本 质提高软件的维护性,通用性和扩展性,并降低软件的复杂度。
3) 、<<设计模式>> 是经典的书,作者是Erich Gamma、Richard Helm、Ralph Johnson和John Vissides Design (俗称“四人组 GOF”)
4)、设计模式并不局限于某种语言, java, php, c++都有设计模式
-
为什么学
学设计模式是为了对软件设计中普遍存在的(反复出现)的各种问题,提出
解决方案
。为了解决某一类问题
编写软件过程中,程序员面临来自耦合性,内聚性以及可维护性,可扩宽性,重用性,灵活性的挑战,设计模式是为了让程序具有更好的
- 代码重用性
- 可读性
- 可扩展性 (当需要增加新的功能,非常方便)
- 可靠性 (增加新的功能,对原来的功能没有影响)
- 使程序呈现高内聚,低耦合的特性
-
有什么用
当一个项目开发完后,如果有客户提出增新功能,使用设计模式会有很好的
扩展性
原来程序员离职,接手项目比较容易
-
怎么用
1)、第1层:刚开始学编程不久,听说过什么是设计模式
2)、第2层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但.是自己却不知道
3)、第3层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的
4)、第4层:阅读了很多别人写的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和 带来的好处。
5)、第5层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来。
一、设计模式的6/7 大原则:
●设计原则
核心思想
1)、找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在–起。
2)、针对接口编程,而不是针对实现编程。
3)、为了交互对象之间的松耦合设计而努力
背过:
1、
开闭原则(Open Close Principle)
对扩展开放(对提供方),对修改关闭(对使用方)。
2、
里氏替换原则(Liskov Substitution Principle)
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在 基类的基础上增加新的行为。
3、
依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。
4、
接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口来降低耦合度。一个类对另一个类的依赖应该建立在最小接口上。
5、
迪米特法则(最少知道原则) (Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、
合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会 被子类修改。
-
单一职责原则
一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1这个类和A2这个类
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性
- 降低变更引起的风险
-
通常情况下,
我们应当遵守单一职责原则
,只有逻辑足够简单,才可以在代码违反单一职责原则;只有类中的方法数量足够少,才可以在方法级别保持单一职责原则
-
接口隔离原则
分割接口
-
客户端不应该依赖它不需要的接口
-
一个类对另一个类的依赖应该建立在最小接口上。
-
-
依赖倒转原则
-
依赖倒转的中心思想是
面向接口编程
。 - 依赖的是抽象不应该依赖细节(具体),细节应该依赖抽象。
-
使用接口
。抽象类的目的是练好
规范
,而不涉及任何具体的
操作
,把展现细节的任务交给
实现类
去完成 -
依赖倒转原则的注意事项和细节。
- 低沉模块尽量都要有抽象类或接口,或者两者都有程序稳定性比较好。
- 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间就存在一个缓冲层,易于程序扩展和优化。
- 继承时遵循里氏替换原则。
-
依赖倒转的中心思想是
-
里氏替换原则
- 所有引用基类的地方必须能透明的使用期间之内的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,**在适当的情况下,可以通过聚合,组合(注入),依赖(接口替换)。来解决问题。**让两个类继承一个更加基础的类。
-
开闭原则ocp
- 开闭原则(Open Closed Principle) 是编程中最基础最重要的设计原则。
-
一个软件实体如类,模块和函数应该对
扩展开放(对提供方)
,对
修改关闭(对使用方)
。用抽象构建框架,用实现扩展细节。 - 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
-
迪米特法则
- 一个对象应该对其他对象保持最少的了解。
- 类与类关系越密切。耦合度越大。
-
迪米特法则(Demeter Principle)又叫
最少知道法则
,及一个类对自己依赖的那。
知道的越少越好
,也就是说对于被依赖人类,不管多么复杂,都尽量将逻辑封装在类的内部。对于除了提供public方法不对外泄露任何信息。 - 迪米特法则。还有个更简单的定义,只与直接的朋友通信。出现成员变量方法参数,方法返回值中的那称为直接的朋友。而出现局部变量中,那我是直接的朋友。陌生的类最好不要以局部变量的形式出现在类的内部。
-
迪米特法则注意事项和细节
- 迪米特法则的核心是降低类之间的耦合。
- 但是注意由于每个类都减少了不必要的依赖。因为迪米特法则只是要求降低,类之间对象间耦合关系并不是要求完全没有依赖关系。
-
合成复用原则
原则是尽量使用合成/聚合的方式,而不是使用继承。
1)、UML类图定义
-
UML–Unified modeling language UML(统一建模语言),是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果
-
UML本身是一套符号的规定,就像数学符号和化学符号-样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等。
-
使用UML来建模,常用的工具有RationalRose,也可以使用一些插件来建模。
Common 常用
Note 对你的UML注释
Class 表示类,可以添加属性和方法。
Interface 表示接口。
Relation
Dependency 依赖。
Association 关联。
Generalization 表示泛化(继承。)
Realization 表示实现。
Aggregation 聚合。
Composite 组合。耦合性要比聚合强。1)、用例图(use case)
2)、静态结构图:
类图
、对象图、包图、组件图、部署图.
3)、动态行为图: 交互图(时序图与协作图)、状态图、活动图类图: 是描述类与类之间的关系的,是UML图中最核心的
2)、UML类图详解
1)、用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系。
2)、类之间的关系: 依赖、泛化(继承)、实现、关联、聚合与组合。
1. 名称详解
依赖(Dependency)
如果有以下方式、我们就称之为依赖
-
、类中用到了对方
2)、如果是类的成员属性
3)、如果是方法的返回类型
4)、是方法接收的参数类型
5)、方法中使用到
只要是在类中用到了对方, 那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了。
public class PersonServiceBean {
private PersonDao personDao;/类
public void save(Person person){}
public IDCard getIDCard(Integer personid){}
public void modifyO{
Depatment department= new Depatment{};
}
}
public class PersonDao{}
public class IDCard{}
public class Person{}
public class Department{}
泛化(Generalization)
泛化关系实际上就是
继承
关系,他是
依赖关系的特例
1)、泛化关系实际上就是继承关系
2)、如果A类继承了B类, 我们就说A和B存在泛化关系
public abstract class DaoSupport{
public void save(Object entity){}
public void delete(Object id){ }
}
public class PersonServiceBean extends Daosupport{}
实现(Realization)
实现关系实际上就是
A类实现B类
,他是
依赖关系的特例
public interface PersonService {
public void delete(Interger id){}
}
public class PersonServiceBean implements PersonService {
public void delete(Interger id){}
}
关联(Association)
关联关系实际上就是类与类之间的联系,他是
依赖关系的特例
关联具有导航性: 即双向关系或单向关系
单向一对一关系 我用到你。
public class Person {
private IDCard card;
}
public class IDCard{}
双向- -对一关系 我用到你,你用到我。
public class Person {
private IDCard card;
}
public class IDCard{
private Person person{ }
}
聚合(Aggregation)
-
聚合关系(Aggregation) 表示的是整体和部分的关系,
整体与部分可以分开
。聚
合关系是关联关系的特例,所以他具有关联的导航性与多重性。 -
如: -台电脑由键盘(keyboard)、显示器(monitor),鼠标等组成;组成电脑的各个
配件是可以从电脑.上分离出来的,使用带空心菱形的实线来表示:
public class Computer {
private Mouse mouse; //鼠标可以和computer分离
private Moniter moniter; //显 示器可以和Computer分离
public void setMouse(Mouse mouse) {
this.mouse = mouse ;
}
public void setMoniter(Moniter moniter) {
this.moniter = moniter;
}
}
没有这两个类(Mouse and Moniter)对Computer类的创建没有影响。
组合(Composite)
-
组合关系:也是整体与部分的关系,但是
整体与部分不可以分开
。实心菱形指向使用的类
只要Person创建了Head就创建了,所以不能分离。人可以没有身份证,但是不能没有头。
public class Person{
private IDCard card;
private Head head = new Head();
public class IDCard{}
public class Head{}
}
-
但是如果在程序中Person实体中
定义
了对IDCard进行级联删除,即删除Person时连同IDCard一起删除,那么IDCard和Person就是组合关系了
二、设计模式分为三种类型23种
对于新手时间比较紧迫的,先追一下这几个,可以去招聘网站上看一下,需求的设计模式
-
创建型模式:
单例模式、建造者模式、工厂模式、
-
结构型模式:
适配器模式、代理模式
-
行为型模式:
模板方法模式、策略模式
设计模式分为三种类型,共23种
-
创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工模式。
-
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
-
行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式) 、状态模式、策略模式、职责链模式(责任链模式)。
注意:不同的书籍.上对分类和名称略有差别
1)、创建型模式
1 创建型模式:
单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
。
学完一个类别的就花时间想想这个类别都有啥,每个模式是干啥的
1. 单例模式、
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个
类只能存在一个对象实例
,并且该类只提供一个取得其对象实例的方法(静态的)。
-
饿汉式(静态常量)
2)
饿汉式(静态代码块)
3) 懒汉式(线程不安全)–不推荐
4) 懒汉式(线程安全,同步方法)–不推荐
5)
懒汉式(线程安全,同步代码块)
6)
双重检查
7)
静态内部类
8)
枚举
-
单例模式注意事项和细节说明
1)、单例模式保证了系统内存中该类只存在一一个对象,节省了系统资源,对于一些需要频繁创建销毁的 对象,使用单例模式可以提高系统性能
2)、当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3)、单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重 量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、 session工厂 ~等)
一、
饿汉式(静态常量)
优缺点说明:
1)、 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
2) 、缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。 如果从始至终从未使用过这个实例,则 会造成内存的浪费
结论:这种单例模式可用,可能造成内存浪费
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01(){}
public static Mgr01 getInstance(){ return INSTANCE; }
public void m(){ System.out.println("m"); }
public static void main(String[] args) {
Mgr01 mg1 = Mgr01.getInstance();
Mgr01 mg2 = Mgr01.getInstance();
System.out.println(mg1.hashCode()+"\n"+mg2.hashCode());
}
}
二、懒汉式、双重检查
1)、 Double-Check概念 是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == nul)检 查,这样就可以保证线程安全了。
2)、这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == nlll),直接return实例化对象,也 避免的反复进行方法同步.
3)、线程安全;延迟加载;效率较高
4)、结论:在实际开发中,推荐使用这种单例设计模式
public class Mgr03 {
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03(){}
public static Mgr03 getInstance(){
if(INSTANCE == null){
synchronized (Mgr03.class) {
if(INSTANCE == null){
INSTANCE = new Mgr03();
return INSTANCE;
}
}
}
return INSTANCE;
}
}
三、静态内部类
1)、这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
2)、静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方 法, 才会装载SingletonInstance类,从而完成Singleton的实例化。
3)、类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类 进行初始化时,别的线程是无法进入的。
4)、优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
5) 、结论:
推荐使用
.
- volatile关键字的作用:保证了变量的可见性(visibility)。被volatile关键字修饰的变量,如果值发生了变更,其他线程立马可见,避免出现脏读的现象。如以下代码片段,isShutDown被置为true后,doWork方法仍有执行。如用volatile修饰isShutDown变量,可避免此问题。
public class Mgr04 {
private static volatile Mgr04 INSTANCE;
private Mgr04(){}
private static class MgrSon{
private static final Mgr04 INSTANCES = new Mgr04();
}
public static Mgr04 getInstance(){
try{
Thread.sleep(1);
}catch(Exception e){
e.printStackTrace();
}
return MgrSon.INSTANCES;
}
public static void main(String[] args) {
for(int i=0;i<1000;i++){
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(getInstance().hashCode());
}
}).start();
}
}
}
四、枚举
1)、这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防 止反序列化重新 创建新的对象。
2)、这种方式是Effective Java作者Josh Bloch提倡的方式
3)、结论:推荐使用
//语法上是最好的,但是是一个枚举,java的创始人之一写的书上的Effective推荐的写法
public enum Mgr {
INSTANCE;
public static void main(String[] args) {
for(int i=0;i<1000;i++){
//有laman简写 效果是一样的 用于实现只有一个方法的接口 ()->{}
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(INSTANCE.hashCode());
}
}).start();
}
}
}
2. 工厂模式、
1)、工厂 模式的意义
将实例化对象的代码提取出来,放到-一个类中统一管理和维护, 达到和主项目的
依赖关系的解耦。从而提高项目的扩展和维护性。
2)、三种工厂 模式(简单工厂模式、工厂方法模式、抽象工厂“模式)
3)、设计 模式的依赖抽象原则
➢ 创建对象实例时,不要直接new类,而是把这个new类的动作放在一一个工厂:的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。
➢不要让类继承具体类,而是继承抽象类或者是实现interface(接口)
➢不要覆盖基类中已经实现的方法。
1. 简单工厂模式
基本介绍:
1)、简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产 品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
2)、简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
3)、在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式
优点是比较好理解,简单易操作。
缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修 改代码,或者尽可能少修改代码.
//披萨类
public abstract class Pizza {
String name;
public void prepare(){ }
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class PepperPizza extends Pizza{
//披萨种类
public void prepare(){
setName("胡椒披萨");
System.out.println("PepperPizza");
}
}
//制作披萨的工厂,只负责制造披萨
public class PizzaFactory {
public Pizza createPizza(String orderType){
Pizza piza = null;
if(orderType.equals("Greek")){
piza = new GreekPizza();
}else if(orderType.equals("Cheese")){
piza = new CheesePizza();
}else if(orderType.equals("Pepper")){
piza = new PepperPizza();
}
return piza;
}
}
//使用工厂制造披萨的类,创建披萨的方式有很多种,这个算是烤的,可能还有冷的、麻辣、香辣、甜...
public class OrdType {
PizzaFactory pizzaFactory;
Pizza pizza;
OrdType(PizzaFactory pizzafact){
this.pizzaFactory = pizzafact;
setPizzaFactory();
}
public void setPizzaFactory(){
String order = "";
do{
order = getType();
pizza = pizzaFactory.createPizza(order);
if(pizza != null){
System.out.println("欢迎制作披萨!");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("制作完成!");
}else{
System.out.println("订购披萨失败,没有您需要的披萨");
break;
}
}while(true);
}
public String getType(){
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
//真正使用的类,可能是控制器,服务层
public class MakePizza {
public static void main(String[] args) {
new OrdType(new PizzaFactory());
System.out.println("Make pizza over!");
}
}
2. 工厂方法模式
工厂方法模式介绍:
1. 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
2. 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
public abstract class Pizza {
String name;
public abstract void prepare();
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class BJCheesePizza extends Pizza{
public void prepare(){
setName("北京奶酪披萨");
System.out.println(name);
}
}
public class BJGreekPizza extends Pizza{
public void prepare(){
setName("北京希腊披萨");
System.out.println(name);
}
}
//使用工厂制造披萨的类,创建披萨的方式有很多种,这个算是烤的,可能还有冷的、麻辣、香辣、甜...
public abstract class OrdType {
public abstract Pizza setPizzaFactory(String order);
//公共的方法由抽象的构造器写出来
OrdType(){
Pizza pizza = null;
String order = null;
do{
order = getType();
pizza = setPizzaFactory(order);//具体方法由子类实现
if(pizza == null){
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("您的"+pizza.name+"请收好!");
}while(true);
}
public String getType() {
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
public class BJOrdType extends OrdType{
@Override
public Pizza setPizzaFactory(String order) {
Pizza pizza = null;
if(order.equals("Cheese")){
pizza = new BJCheesePizza();
}else if(order.equals("Greek")){
pizza = new BJGreekPizza();
}else{
System.out.println("没有你要的种类");
}
return pizza;
}
}
//制作披萨的抽象工厂类,
public abstract class PizzaFactory {
public abstract Pizza createPizza(String orderType);
}
public class BJPizzaFactory extends PizzaFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("Cheese")){
pizza = new BJCheesePizza();
}else{
System.out.println("您要的披萨不存在!");
}
return pizza;
}
}
public class TestBjPizza {
public static void main(String[] args) {
new BJOrdType();
System.out.println("结束!");
}
}
3. 抽象工厂模式
1)、抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
2)、抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
3)、从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进- -步 的抽象)。
4)、将工厂抽象成两层,AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建对象类型使用对 应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
5)、类图
public abstract class Pizza {
String name;
public abstract void prepare();
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class BJCheesePizza extends Pizza{
public void prepare(){
setName("北京奶酪披萨");
System.out.println(name);
}
}
public class BJGreekPizza extends Pizza{
public void prepare(){
setName("北京希腊披萨");
System.out.println(name);
}
}
//接口
public interface IPizzaFactory {
Pizza createPizza(String orderType);
}
public class BJFactory implements IPizzaFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("Cheese")){
pizza = new BJCheesePizza();
}else if(orderType.equals("Greek")){
pizza = new BJGreekPizza();
}else{
System.out.println("您要的披萨不存在!");
}
return pizza;
}
}
public class BJOrdType {//北京工厂子类,伦敦的同理
IPizzaFactory iPizzaFactory;
OrdType(IPizzaFactory Factory){
setiPizzaFactory(Factory);
}
//只要是成员变量都是要有set方法的,不然就没有意义
private void setiPizzaFactory(IPizzaFactory factory) {
this.iPizzaFactory = factory;
Pizza pizza= null;
String order = null;
do{
order = getType();//这里的工厂可能是北京的,也有可能是伦敦的
pizza = iPizzaFactory.createPizza(order);
if(pizza == null){
break;
}
System.out.println("使用抽象工厂模式");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("您的"+pizza.name+"请收好!");
}while(true);
}
public String getType() {
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
public class TestFactory {
//测试类
public static void main(String[] args) {
new OrdType(new BJFactory());
System.out.println("结束~!");
}
}
运行结果
input pizza type:
Greek
使用抽象工厂模式
北京希腊披萨
北京希腊披萨------正在烤
北京希腊披萨------正在切!
北京希腊披萨------打包!
您的北京希腊披萨请收好!
input pizza type:
asdf
您要的披萨不存在!
结束~!
4. 原型模式、
1)、原型模式(Prototype模式)是指: 用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2)、原型模式是一种创建型设计模式,允许-一个对象再创建另外-一个可定制的对象无需知道如何创建的细节
3)、工作原理是:通过将-一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象 拷贝它们自己来实施创建,即对象.clone()
4)、形象的理解:孙大圣拔出猴毛,变出其它孙大圣
原型模式实现类的克隆,如果说后面类多了一个属性,克隆的属性依然会有。
① 浅拷贝关于对象是引用的。
-
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将
该属性值复制- – 份给新的对象。 -
对于数据类型是引用数据类型的成员变量,比如说成员变量是
某个数组、某个类
的对象
等,那么
浅拷贝会进行引用传递
,也就是只是将该成员变量的引用值(内
存地址)复制一.份给新的对象。因为实际上两个对象的该成员变量都指向同-一个
实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成
员变量值 -
浅拷贝是使用默认的clone()方法来实现
sheep = (Sheep) super.clone();
// 需要实现Cloneable接口
public class Sheep implements Cloneable{
private int age;
private String name;
private String color;
public Sheep friend;
@Override //从写Object的clone类
protected Object clone(){ //如果属性只有基本数据类型,那么默认超类方法即可
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
}catch(Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"white");
sheep.friend = new Sheep("jack",2,"black");
Sheep sheep1 = (Sheep)sheep.clone();
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
System.out.println(sheep+"------ sheep.friend="+sheep.friend.hashCode());
System.out.println(sheep1+"------ sheep.friend="+sheep1.friend.hashCode());
System.out.println(sheep2+"------ sheep.friend="+sheep2.friend.hashCode());
System.out.println(sheep3+"------ sheep.friend="+sheep3.friend.hashCode());
}
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
② 深拷贝基本介绍
- 复制对 象的所有基本数据类型的成员变量值
-
为 所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变
量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对
整个对象进行拷贝 - 深拷贝实现方式1: 重写clone方法来实现深拷贝,
- 深拷贝实现方式2: 通过对象序列化实现深拷贝
方式一:
public class Sheep implements Cloneable,Serializable{
private int age;
private String name;
private String color;
public Sheep1 friend;
@Override
protected Object clone(){
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
sheep.friend = (Sheep1)friend.clone();
}catch(Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
//friend 引用指向重写父类clone方法
public class Sheep1 implements Serializable,Cloneable{
String sheep1 = "sheep1";
@Override
public String toString() {
return "Sheep1 [sheep1=" + sheep1 + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
方式二、
- 使用的是流的方式,进行序列化和反序列化,转成字节,在转成对象,即时字节是一样的,但是转出来的对象hashCode()也会不同,
public class Sheep1 implements Serializable{
String sheep1 = "sheep1";
@Override
public String toString() {
return "Sheep1 [sheep1=" + sheep1 + "]";
}
}
//只要实现Serializable接口就行了。
public class Sheep implements Serializable{
private int age;
private String name;
private String color;
public Sheep1 friend;
//方式二、运用流存到内存的序列化原理,进行读写操作来克隆
Sheep copy(){
ObjectInputStream ois = null;
ByteArrayInputStream bais = null;
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try{//可以这样理解,对象转换到字节叫序列化,写出去叫out
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
//write is a Object,this is 当前 Object
oos.writeObject(this);
//相当于转成了字节,然后把字节转成Object回来,叫反序列化,
//baos.toByteArray()意思是拷贝一份刚刚序列化的
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
//读的是Object method name is readObject()
Sheep sheep = (Sheep)ois.readObject();
return sheep;
}catch(Exception e){
System.out.println(e.getMessage());
}
try{//记得要关闭流
ois.close();
bais.close();
oos.close();
baos.close();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=21598637
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=26887603
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=10069385
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=3615232
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=14519747
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=17033014
原型模式的优缺点:
1)、创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2)、 不用重新初始化对象,而是动态地获得对象运行时的状态
3) 、如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的编号,无需修改代码
4) 、在实现深克隆的时候可能需要比较复杂的代码
5) 、缺点:需要为每- -个类配备-一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要 修改其源代码,违背了ocp原则,这点请同学们注意.
5. 建造者模式、
- 建造者模式的注意事项和细节
1)、客户端(使用程序)
不必知道产品内部组成的细节
,将产品本身与产品的
创建过程解耦
,使得
相同的创建过程 可以创建不同的产品对象
2)、每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户 使用不同的具体建造者即可得到不同的产品对象
3)、可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
4)、增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”
5)、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,
如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到—定的限制
。
6)、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.
7)、抽象工厂模式VS建造者模式抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用
抽象工厂模式不需要关心构建过程
,只关心什么产 品由什么工厂生产即可。而
建造者模式则是要求按照指定的蓝图建造产品
,它的主要目的是通过组装零配件而产生一个新产品
建造者模式的四个角色:
1)、 Product (
产品
角色) : – 一个 具体的产品对象。
2) 、Builder (
抽象建造者
) :创建一个Product对象的各个部件指定的接口/抽象类。
3)、 ConcreteBuilder (
具体建造者
) :实现接口,构建和装配各个部件。
4) 、Director (
指挥者
) :构建- -个使用Builder接口的对象。它主要是用于创建- -个复杂的对象。它主要 有 两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
public class House {
private String baise;
private String wall;
private String roofed;
//补上set、get、toString方法
}
public abstract class HouseBuilder {
//建造房子的抽象类
protected House house = new House();
public House getHouse() {
return house;
}
public abstract void builderBaise();
public abstract void builderwall();
public abstract void roofed();
}
public class CommonHouse extends HouseBuilder{
//普通房子
@Override
public void builderBaise() {
house.setBaise("地基5m!");
System.out.println("打普通地基5m深!");
}
@Override
public void builderwall() {
house.setWall("砌墙10cm!");
System.out.println("砌普通墙10cm厚!");
}
@Override
public void roofed() {
house.setRoofed("屋顶3m!");
System.out.println("盖普通屋顶3m高!");
}
}
public class HigthBuilder extends HouseBuilder {
//高楼
@Override
public void builderBaise() {
house.setBaise("地基25m!");
System.out.println("打高楼地基25m深!");
}
@Override
public void builderwall() {
house.setWall("砌墙20m!");
System.out.println("砌高楼墙20cm厚!");
}
@Override
public void roofed() {
house.setRoofed("屋顶9m!");
System.out.println("盖高楼屋顶9m高!");
}
}
public class DirectorHouse {
//指挥者
HouseBuilder houseBuilder = null;
DirectorHouse(HouseBuilder houseBuild){
this.houseBuilder = houseBuild;
}
public void setHouseBuilder(HouseBuilder houseBuild) {
this.houseBuilder = houseBuild;
}
//建造顺序由指挥者来决定
House BuilderHouse(){
houseBuilder.builderBaise();
houseBuilder.builderwall();
houseBuilder.roofed();
return houseBuilder.getHouse();
}
}
public class Client {
//盖房子的客户 需求方
public static void main(String[] args) {
//盖普通房子
DirectorHouse dh = new DirectorHouse(new CommonHouse());
dh.setHouseBuilder(new CommonHouse());
House house = dh.BuilderHouse();
System.out.println(house+"\n-----------------------------");
//盖高楼
dh.setHouseBuilder(new HigthBuilder());
System.out.println(dh.BuilderHouse());
}
}
2)、结构型模式
2 结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
。
1. 适配器模式
-
基本介绍
1)、适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,
主的目的是兼容
性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
2)、适配器模式属于结构型模式
3)、主要分为三类:
类适配器模式、对象适配器模式、接口适配器模式
-
工作原理
1)、 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
2)、从用户的角度看不到被适配者,是解耦的
3)、用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法 -
适配器模式的注意事项和细节
1)、三种命名方式,是根据src是以怎样的形式给到Adapter (在Adapter里的形式)来命名的。
2)、类适配器:以类给到,在Adapter里, 就是将src当做类,继承对象适配器:以对象给到,在Adapter 里,将src作为-一个对象,持有接口适配器:以接口给到,在Adapter里,将srC作为一个接口,实现
3)、 Adapter模式最大的作用还是将原本不兼容的接口融合在- – 起工作。
4)、实际开发中,实现起来不拘泥于我们讲解的三种经典形式
① 类适配器模式、
-
类适配器模式注意事项和细节
1)、 Java是 单继承机制,所以类适配器需要继承src类这一点算是- 一个缺点,因为这要求dst必须是接口, 有一-定局限性;
2) 、src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
3)、由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
public class Voltage220v {
//电源电压
public int Voltage220(){
int src = 220;
System.out.println("获得电压"+src+"伏");
return src;
}
}
public interface Voltage {
//电源转换接口
int charging5v();
int charging20v();
}
public class VoltageAdatper extends Voltage220v implements Voltage{
//电源转接口
public int charging5v() {
int src = Voltage220();
return src/44;
}
public int charging20v() {
return 0;
}
}
public class Phone {
//手机
void charging(Voltage voltage5v){
int src = voltage5v.charging5v();
if(src == 5){
System.out.println("转成5 伏,充电成功!!");
}else{
System.out.println("转化失败!");
}
}
}
public class Client {
//客户
public static void main(String[] args) {
Phone ph = new Phone();
ph.charging(new VoltageAdatper());
System.out.println("Charging over!");
}
}
② 对象适配器模式、
-
对象适配器模式注意事项和细节
1)、对象适配器和类适配器其实算是同- -种思想,只不过实现方式不同。根据合成复用原则,使用组合替 代继承,所以它解决 了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
2) 、使用成本更低,更灵活。
public class Voltage220v {
//电源电压
public int Voltage220(){
int src = 220;
System.out.println("获得电压"+src+"伏");
return src;
}
}
public interface Voltage {
//电源转换接口
int charging5v();
int charging20v();
}
public class VoltageAdatper implements Voltage{
//电源转接口 使用的是聚合关系
protected Voltage220v voltage220v;
VoltageAdatper(Voltage220v voltage220){
this.voltage220v = voltage220;
}
public int charging5v() {
if(voltage220v == null){
System.out.println("exit!");
return;
}
int src = voltage220v.Voltage220();
return src/44;
}
public int charging20v() {
return 0;
}
}
public class Phone {
//手机
void charging(Voltage voltage5v){
int src = voltage5v.charging5v();
if(src == 5){
System.out.println("转成5 伏,充电成功!!");
}else{
System.out.println("转化失败!");
}
}
}
public class Client {
//客户
public static void main(String[] args){
System.out.println("对象适配器");
Phone ph = new Phone();
VoltageAdatper v = new VoltageAdatper(new Voltage220v());
ph.charging(v);
System.out.println("Charging over!");
}
}
③ 接口适配器模式、
-
接口适配器模式介绍
1)、一些 书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
2)、当
不需要全部实现接口提供的方法
时,可先设计一个抽象类实现接口,并为该接口中每个方法
提供一 个默认实现(空方法)
,那么该抽象类的子类可有选择地
覆盖父类的某些方法来实现需求
3)、适用于一个接口不想使用其所有的方法的情况。
public interface Interface {
void m1();
void m2();
void m3();
void m4();
}
public abstract class AbstartAdatper implements Interface {
@Override
public void m1(){ }
@Override
public void m2(){ }
@Override
public void m3(){ }
@Override
public void m4(){ }
}
public class Client {
public static void main(String[] args) {
AbstartAdatper aba = new AbstartAdatper(){
@Override
public void m1(){
System.out.println("接口适配器");
}
};
aba.m1();
}
}
2. 桥接模式
3. 装饰模式
4. 组合模式
5. 外观模式
6. 享元模式
7. 代理模式
-
代理模式的基本介绍
1)、代理模式:为一个对象提供-一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样 做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2)、被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
3)、代理模式有不同的形式,主要有三种
静态代理
、
动态代理
(JDK代理、接口代理)和
Cglib代理
(可以在内 存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)。
①、静态代理
-
静态代码模式的基本介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
应用实例:
➢具体要求
1)、定义一个接口:ITeacherDao
2)、目标对象TeacherDAO实现接口ITeacherDAO
3) 、使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
4) 、调用的时候通过调用代理对象的方法来调用目标对象.
5) 、特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。 -
静态代理优缺点
1)、优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展.
2)、缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
3)、一旦接口增加方法,目标对象与代理对象都要维护
public interface ITeacher {
void teacher();
}
public class Teacher implements ITeacher{
//老师类
public void teacher() {
System.out.println("语文老师原教课内容!");
}
}
public class TeacherProxy implements ITeacher{
private ITeacher iTeacher;
TeacherProxy(ITeacher iTeacher){
this.iTeacher = iTeacher;
}
public void teacher(){
System.out.println("新老师擦黑板!");
iTeacher.teacher();
System.out.println("下课了,关上门走了!");
}
}
public class Client {
public static void main(String[] args) {
new TeacherProxy(new Teacher()).teacher();
}
}
②、动态代理
-
动态代理模式的基本介绍
1)、代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
2)、代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
3)、动态代理也叫做: JDK代理、 接口代理 -
JDK中生成代理对象的API
1)、代理类所在包:java.lang.reflect.Proxy
2) 、JDK实现代理 只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完
整的写法是: .
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )-
参数1
: loader , 类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存。
一般情况:
当前类.class . getClassLoader();
目标类实例 .getClass().getInterface(); -
参数2
: Class[] interfaces 代理类需要实现的所有接口
方式1:**目标类实例. getClass(). getInterfaces() ;**注意: 只能获得自己接口,不能获得父元素接口
方式2: new Class []{UserService.class}
例如: jdbc驱动–> DriverManager获得接口 Connection -
参数3
: InvocationHandler处理类, 接口,必须进行实现类,-般采用匿名内部
提供invoke方法,代理类的每一个方法执行时,都将调用一次invoke-
参数3.1
:
Object proxy :代理对象
-
参数3.2
: Method method :代理 对象当前执行的方法的描述对象(反射)
执行方法名: method. getName( )
执行方法:
method. invoke(对象,实际参数)
-
参数3.3
:
0bject[] args :方法实际参数
-
-
public interface ITeacher {
void teacher();
void mathTea();
}
public class Teacher implements ITeacher{
//老师类
public void teacher() {
System.out.println("语文老师原教课内容!");
}
public void mathTea(){
System.out.println("数学老师原教课内容!");
}
}
public class ProxyFactory {
private ITeacher iTeacher;
ProxyFactory(ITeacher iTeacher){
this.iTeacher = iTeacher;
}
public Object getInstance(){
return Proxy.newProxyInstance(
ITeacher.class.getClassLoader(),
iTeacher.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("执行老师方法前!");
Object obj = method.invoke(iTeacher, args);
System.out.println("当然这句话可以执行其他方法!");
return obj;
}
});
}
}
public class Client {
public static void main(String[] args) {
ITeacher iTeacher = (ITeacher)new ProxyFactory(new Teacher()).getInstance();
iTeacher.mathTea();
iTeacher.teacher();
}
}
③、Cglib代理
-
Cglib代理模式的基本介绍
1)、静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象, 并
没有实现任何的接口
,这个时候可使用目标对象子类来实现代理这就是Cglib代理
2) 、Cglib代理 也叫作子类代理,它是在内存中构建一个子类 对象从而实现对目标对象功能扩展,有些书也 将Cglib代理归属到动态代理。
3)、 Cglib是一 一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许 多AOP的框架使用,例如Spring AOP,实现方法拦截
4)、在AOP编程中如何选择代理模式:1. **目标对象需要实现接口,用JDK代理** 2. **目标对象不需要实现接口,用Cglib代理**
5)、Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
2020-4-6日在Mybatis 中找的jar包也可以运行
asm-3.3.1.jar
cglib-2.2.2.jar
commons-logging-1.1.1.jar
public class TeacherDao {
public void teacher(){
System.out.println("教书,cglibProxy not need Interface!");
}
public void mathTeacher(){
System.out.println("Math class.cglibProxy not need Interface!");
}
}
public class ProxyFactory implements MethodInterceptor{
private Object object;
ProxyFactory(Object Obj){
this.object = Obj;
}
public void setTeacherDao(Object Obj) {
this.object = Obj;
}
public Object getInstance(){
//1. 创建一个工具类
Enhancer enhancer = new Enhancer();
//2. 拿到他的class,设置它的父类。
enhancer.setSuperclass(object.getClass());
//3. 设置回调函数。
enhancer.setCallback(this);
//4. 创建子类对象,及代理对象。
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] arg2,MethodProxy arg3) throws Throwable {
// 未知 方法 形参
//抽象类底层封装的自动调用 intercept,默认对没个方法都进行了封装
System.out.println("开始调用Cglib代理对象!");
Object obj = method.invoke(object, arg2);
System.out.println("调用代理对象结束。");
return null;
}
}
public class Client {
public static void main(String[] args) {
ProxyFactory proxy = new ProxyFactory(new TeacherDao());
TeacherDao tea = (TeacherDao)proxy.getInstance();
tea.mathTeacher();
tea.teacher();
}
}
④、常见代理模式介绍
1)、防火墙代理 .
内网通过代理穿透防火墙,实现对公网的访问。
2)、缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数 据库取,然后缓存。
3)、远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象 沟通信息。
4)、同步代理:主要使用在多线程编程中,完成多线程间同步工作
3)、行为型模式
3)行为型模式:
模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式) 、状态模式、策略模式、职责链模式(责任链模式)
。
1. 模版方法模式
-
基本介绍.
1)、模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern), Z在一个抽象类公开 定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进 行。
2)、简单说,模板方法模式定义一个操作中的算法的骨架,而将- – 些步骤延迟到子类中,使得子类可以不 改变-一个算法的结构,就可以重定义该算法的某些特定步骤
3)、这种类型的设计模式属于行为型模式。 -
模板方法模式的注意事项和细节
1)、基本思想是:
算法只存在于一个地方,也就是在父类中
,容易修改。需要修改算法时,只要修改父类 的模板方法或者已经实现的某些步骤,子类就会继承这些修改
2)、实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
3)、 既统- -了 算法,也提供了很大的灵活性。父类的模板方法确保 了算法的结构保持不变,同时由子类 提供部分步骤的实现。
4)、该模式的不足之处:每一个不同的实现都需要-一个子类实现,导致类的个数增加,使得系统更加庞大
5)、
一般模板方法都加上final关键字,防止子类 重写模板方法.
6)、模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相 同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理模板方法模式的注意事项和细节
① 普通模板方法模式
public abstract class SoyaMilk {
//final can't writer son class
final void make(){
System.out.println("Making Soya Milk start!");
select();
add();
soak();
beat();
}
abstract void select();
final void add(){
System.out.println("add Make Milk material!");
}
final void soak(){
System.out.println("soak on the water!");
}
final void beat(){
System.out.println("beat over finish!");
}
}
public class Pennut extends SoyaMilk{
@Override
void select() {
System.out.println("select big Soya!");
}
}
public class Client {
public static void main(String[] args) {
SoyaMilk soyaMilk = new Pennut();
soyaMilk.make();
}
}
② 钩子方法
-
模板方法模式的钩子方法.
1)、在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖 它,该方法称为“钩子”。
2)、还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,
不添加任何的配料
,请使用钩子方 法对前面的模板方法进行改造
public abstract class SoyaMilk {
//final can't writer son class
final void make(){
select();
if(isPure()){
add();
}
soak();
beat();
}
abstract void select();
final void add(){
System.out.println("add Make Milk material!");
}
final void soak(){
System.out.println("soak on the water!");
}
final void beat(){
System.out.println("beat over finish!");
}
//可以设置需要还是不需要调用的方法
boolean isPure(){
return true;
}
}
public class PureMilk extends SoyaMilk{
@Override
void select() {
System.out.println("select big Soya!");
}
@Override
boolean isPure(){
return false;
}
}
public class Client {
public static void main(String[] args) {
SoyaMilk soyaMilk = new Pennut();
soyaMilk.make();
System.out.println("--------------------------");
SoyaMilk pureMilk = new PureMilk();
pureMilk.make();
}
}
2. 命令模式
3. 访问者模式
4. 迭代器模式
5. 观察者模式
6. 中介者模式
7. 备忘录模式
8. 解释器模式(Interpreter模式)
9. 状态模式
10. 策略模式
-
基本介绍
1)、策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算 法的变化独立于使用算法的客户
2)、这算法体现了几个设计原则, - 第一、把变化的代码从不变的代码中分离出来;
- 第二、针对接口编程而不具体类(定义了策略接口) ;
-
第三、
多用组合/聚合少用继承
(客户通过组合方式使用策略)。
策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体
设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模
式让行为的变化独立于算法的使用者
public abstract class Duck {
Flybeha flybeha;
Duck(){
disPlay();
quack();
swim();
}
abstract void disPlay();
void quack(){
System.out.println("鸭子会叫~~~");
}
void swim(){
System.out.println("鸭子会游泳~~~");
}
void fly(){
if(flybeha != null){
flybeha.Fly();
}else{
System.out.println("fly failed");
}
}
public void setFlybeha(Flybeha flybeha) {
this.flybeha = flybeha;
}
}
public interface Flybeha {
//接口里面写上所有鸭子的飞翔能力描述,让子类去实现
//将来要添加什么鸭子,直接在写个类实现接口就行了
void Fly();
}
//各种飞翔能力实现类
public class GoodFly implements Flybeha{
@Override
public void Fly() {
System.out.println("this a Duck fly very high!");
}
}
public class NoFly implements Flybeha{
@Override
public void Fly() {
System.out.println("该物种不能飞!");
}
}
public class BadFly implements Flybeha {
@Override
public void Fly() {
System.out.println("this Duck fly is bad~");
}
}
//野鸭
public class WildDuck extends Duck{
WildDuck(){
flybeha = new GoodFly();
}
@Override
void disPlay() {
System.out.println("this is a wildDuck");
}
}
public class ToyDuck extends Duck{
//用到的话,改一下实现类就行了,对于扩展比较方便
ToyDuck(){
flybeha = new NoFly();
}
@Override
void disPlay() {
System.out.println("this is a ToyDuck");
}
}
● 策略模式的注意事项和细节
1)、策略模式的关键是:分析项目中变化部分与不变部分
2)、策略模式的核心思想是:多用组合/聚合少用继承;用行为类组合,而不是行为的继承。更有弹性
3)、体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行 为)即可,避免了使用多重转移语句(if…else if.else)
4)、提供了可以替换继承关系的办法:策略模式将 算法封装在独立的Strategy类中使得你可以独立于其Context 改变它,使它易于切换、易于理解、易于扩展
5)、需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
11. 职责链模式(责任链模式)、