湖南网站制作外包,学校部门网站建设方案书,手机wap浏览器,桐乡网站开发https://www.runoob.com/design-pattern/factory-pattern.html
单例模式
**意图#xff1a;**保证一个类仅有一个实例#xff0c;并提供一个访问它的全局访问点。 **主要解决#xff1a;**一个全局使用的类频繁地创建与销毁。 **何时使用#xff1a;**当您想控制实例数目…https://www.runoob.com/design-pattern/factory-pattern.html
单例模式
**意图**保证一个类仅有一个实例并提供一个访问它的全局访问点。 **主要解决**一个全局使用的类频繁地创建与销毁。 **何时使用**当您想控制实例数目节省系统资源的时候。 **如何解决**判断系统是否已经有这个单例如果有则返回如果没有则创建。 **关键代码**构造函数是私有的。 应用实例 1、一个班级只有一个班主任。 2、Windows 是多进程多线程的在操作一个文件的时候就不可避免地出现多个进程或线程同时操作一个文件的现象所以所有文件的处理必须通过唯一的实例来进行。 使用场景
1、要求生产唯一序列号。2、WEB 中的计数器不用每次刷新都在数据库里加一次用单例先缓存起来。3、创建的一个对象需要消耗的资源过多比如 I/O 与数据库的连接等
优点 1、在内存里只有一个实例减少了内存的开销尤其是频繁的创建和销毁实例比如管理学院首页页面缓存。 2、避免对资源的多重占用比如写文件操作。 缺点没有接口不能继承与单一职责原则冲突一个类应该只关心内部逻辑而不关心外面怎么样来实例化。 注意 1、单例类只能有一个实例。 2、单例类必须自己创建自己的唯一实例。 3、单例类必须给所有其他对象提供这一实例。
实现
饿汉模式
类加载的同时, 创建实例.
public class SingletonHungry {//static 修饰成员变量全局只有一个private static SingletonHungry instance new SingletonHungry();//构造方法私有化使类对象只有一个private SingletonHungry() {}
// 对外提供一个获取获取实例对象的方法
// 用static修饰方法public static SingletonHungry getInstance(){return instance;}
}懒汉模式-单线程版
类加载的时候不创建实例. 第一次使用的时候才创建实例.
public class SingletonLazy {private static SingletonLazy instance null;private SingletonLazy() {}public static SingletonLazy getInstance() {if (instance null) {instance new SingletonLazy();}return instance;}
}懒汉模式-多线程版
上面的懒汉模式的实现是线程不安全的. 线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致 创建出多个实例. 一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改 instance 了) 加上 synchronized 可以改善这里的线程安全问题.
public class SingletonLazy2 {private static SingletonLazy2 instance null;private SingletonLazy2() {}// 以下两种方法都可以
// 在获取成员变量时,先判断锁是否被占用
//
// 其实synchronized代码块只需要执行一次就够了,以现在的写法,只要调用了getInstance方法,都要竞争锁,锁竞争是非常耗费系统资源的
// 使用了synchronized就从用户态转到了内核态public static synchronized SingletonLazy2 getInstance() {if (instance null) {
// 初始化过程只执行一次instance new SingletonLazy2();}return instance;}public static SingletonLazy2 getInstance1() {synchronized(SingletonLazy2.class) {if (instance null) {instance new SingletonLazy2();}return instance;}}// 错误的!!!!!!!!!!!!
// public static SingletonLazy2 getInstance() {
// if (instance null) {
// 此时已经判断instance为空,争抢锁之后就会创建一个新的实例对象
// synchronized (SingletonLazy2.class){
// instance new SingletonLazy2();
// }
// }
// return instance;
// }}懒汉模式-多线程版(改进)
以下代码在加锁的基础上, 做出了进一步改动:
使用双重 if 判定, 降低锁竞争的频率.给 instance 加上了 volatile.
/*** 使用双重 if 判定, 降低锁竞争的频率.* 给 instance 加上了 volatile.** 加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.* 因此后续使用的时候, 不必再进行加锁了.* 外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.* 同时为了避免 内存可见性 导致读取的 instance 出现偏差, 于是补充上 volatile .* 当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,* 其中竞争成功的线程, 再完成创建实例的操作.* 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.*/
//双重检查锁 DCL
public class SingletonDCL {//synchronized只能保证原子性和可见性,不能保证有序性(其他线程可能得到一个创建了对象(instance ! null),但没有得到某些数据初始化的对象)//加上volatile保证有序性(可见性与有序性)private volatile static SingletonDCL instance null;private SingletonDCL() {}public static SingletonDCL getInstance() {//为了让后面的线程不再获取锁,避免锁竞争if (instance null) {synchronized (SingletonDCL.class) {//完成初始化操作,只执行一次if (instance null) {instance new SingletonDCL();}}}return instance;}
}
关于单例模式的饿汉和懒汉模式
工作中可以使用饿汉模式因为书写简单且不易出现错饿汉模式在程序加载时完成的初始化但是由于计算机资源有限为了节约资源可以使用懒汉模式懒汉模式就是在使用对象时再去完成初始化操作懒汉模式在多线程模式可能出现线程安全问题那么就需要使用synchronized包裹初始化代码块初始化代码只执行一次后序的线程在调用getInstance()时依然会产生竞争锁频繁进行用户态和内核态的切换非常浪费所资源这时候就是可以用double check lockDCL)的方式在外层加一个非空校验避免无用的锁竞争synchronized只能保证原子性和可见性,不能保证有序性(其他线程可能得到一个创建了对象(instance ! null),但没有得到某些数据初始化的对象)再使用volatile解决有序性问题描述指令重排序可能出现的问题
工厂模式
**意图**定义一个创建对象的接口让其子类自己决定实例化哪一个工厂类工厂模式使其创建过程延迟到子类进行。 **主要解决**主要解决接口选择的问题。 **何时使用**我们明确地计划不同条件下创建不同实例时。 **如何解决**让其子类实现工厂接口返回的也是一个抽象的产品。 **关键代码**创建过程在其子类执行。 应用实例 1、您需要一辆汽车可以直接从工厂里面提货而不用去管这辆汽车是怎么做出来的以及这个汽车里面的具体实现。 优点 1、一个调用者想创建一个对象只要知道其名称就可以了。 2、扩展性高如果想增加一个产品只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现调用者只关心产品的接口。 **缺点**每次增加一个产品时都需要增加一个具体类和对象实现工厂使得系统中类的个数成倍增加在一定程度上增加了系统的复杂度同时也增加了系统具体类的依赖。这并不是什么好事。 使用场景 1、日志记录器记录可能记录到本地硬盘、系统事件、远程服务器等用户可以选择记录日志到什么地方。 2、数据库访问当用户不知道最后系统采用哪一类数据库以及数据库可能有变化时。 3、设计一个连接服务器的框架需要三个协议“POP3”、“IMAP”、“HTTP”可以把这三个作为产品类共同实现一个接口。 **注意事项**作为一种创建类模式在任何需要生成复杂对象的地方都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式而简单对象特别是只需要通过 new 就可以完成创建的对象无需使用工厂模式。如果使用工厂模式就需要引入一个工厂类会增加系统的复杂度。 工厂模式包含以下几个核心角色
抽象产品Abstract Product定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口规定了产品对象的共同方法。具体产品Concrete Product实现了抽象产品接口定义了具体产品的特定行为和属性。抽象工厂Abstract Factory声明了创建产品的抽象方法可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。具体工厂Concrete Factory实现了抽象工厂接口负责实际创建具体产品的对象。
实现
public static void main(String[] args) {ShapeFactory shapeFactory new ShapeFactory();//获取 Circle 的对象并调用它的 draw 方法Shape shape1 shapeFactory.getShape(CIRCLE);//调用 Circle 的 draw 方法shape1.draw();//获取 Rectangle 的对象并调用它的 draw 方法Shape shape2 shapeFactory.getShape(RECTANGLE);//调用 Rectangle 的 draw 方法shape2.draw();//获取 Square 的对象并调用它的 draw 方法Shape shape3 shapeFactory.getShape(SQUARE);//调用 Square 的 draw 方法shape3.draw();}模板模式
在模板模式Template Pattern中一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
意图定义一个操作中的算法的骨架而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决一些方法通用却在每一个子类都重新写了这一方法。 何时使用有一些通用的方法。 如何解决将这些通用算法抽象出来。 关键代码在抽象类实现其他步骤在子类实现。 应用实例 1、在造房子的时候地基、走线、水管都一样只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持将一些已经定好的方法封装起来比如开启事务、获取 Session、关闭 Session 等程序员不重复写那些已经规范好的代码直接丢一个实体就可以保存。
优点 1、封装不变部分扩展可变部分。 2、提取公共代码便于维护。 3、行为由父类控制子类实现。
缺点每一个不同的实现都需要一个子类来实现导致类的个数增加使得系统更加庞大。
使用场景 1、有多个子类共有的方法且逻辑相同。 2、重要的、复杂的方法可以考虑作为模板方法。
注意事项为防止恶意操作一般模板方法都加上 final 关键词。
实现
我们将创建一个定义操作的 Game 抽象类其中模板方法设置为 final这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类它们重写了抽象类的方法。 TemplatePatternDemo我们的演示类使用 Game 来演示模板模式的用法。
public abstract class Game {abstract void initialize();abstract void startPlay();abstract void endPlay();//模板public final void play(){//初始化游戏initialize();//开始游戏startPlay();//结束游戏endPlay();}
}public class Cricket extends Game {Overridevoid endPlay() {System.out.println(Cricket Game Finished!);}Overridevoid initialize() {System.out.println(Cricket Game Initialized! Start playing.);}Overridevoid startPlay() {System.out.println(Cricket Game Started. Enjoy the game!);}
}public class Football extends Game {Overridevoid endPlay() {System.out.println(Football Game Finished!);}Overridevoid initialize() {System.out.println(Football Game Initialized! Start playing.);}Overridevoid startPlay() {System.out.println(Football Game Started. Enjoy the game!);}
}public class TemplatePatternDemo {public static void main(String[] args) {//直接调用抽象类中的模板方法模板方法中执行子类实现的方法Game game new Cricket();game.play();System.out.println();game new Football();game.play(); }
}策略模式
意图定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 主要解决在有多种算法相似的情况下使用 if…else 所带来的复杂和难以维护。 何时使用一个系统有许多许多类而区分它们的只是他们直接的行为。 如何解决将这些算法封装成一个一个的类任意地替换。 关键代码实现同一个接口。 应用实例 1、诸葛亮的锦囊妙计每一个锦囊就是一个策略。 2、旅行的出游方式选择骑自行车、坐汽车每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
优点 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。 缺点 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景 1、如果在一个系统里面有许多类它们之间的区别仅在于它们的行为那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为如果不用恰当的模式这些行为就只好使用多重的条件选择语句来实现。 策略模式包含以下几个核心角色
环境Context维护一个对策略对象的引用负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。抽象策略Abstract Strategy定义了策略对象的公共接口或抽象类规定了具体策略类必须实现的方法。具体策略Concrete Strategy实现了抽象策略定义的接口或抽象类包含了具体的算法实现。
策略模式通过将算法与使用算法的代码解耦提供了一种动态选择不同算法的方法。客户端代码不需要知道具体的算法细节而是通过调用环境类来使用所选择的策略。
实现
我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。 StrategyPatternDemo我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。
public interface Strategy {public int doOperation(int num1, int num2);
}public class OperationAdd implements Strategy{Overridepublic int doOperation(int num1, int num2) {return num1 num2;}
}public class OperationSubtract implements Strategy{Overridepublic int doOperation(int num1, int num2) {return num1 - num2;}
}public class OperationMultiply implements Strategy{Overridepublic int doOperation(int num1, int num2) {return num1 * num2;}
}public class Context {private Strategy strategy;public Context(Strategy strategy){this.strategy strategy;}public int executeStrategy(int num1, int num2){return strategy.doOperation(num1, num2);}
}public class StrategyPatternDemo {public static void main(String[] args) {Context context new Context(new OperationAdd()); System.out.println(10 5 context.executeStrategy(10, 5));context new Context(new OperationSubtract()); System.out.println(10 - 5 context.executeStrategy(10, 5));context new Context(new OperationMultiply()); System.out.println(10 * 5 context.executeStrategy(10, 5));}
}代理模式
意图为其他对象提供一种代理以控制对这个对象的访问。 主要解决在直接访问对象时带来的问题。 何时使用想在访问一个类时做一些控制。 如何解决增加中间层。 关键代码实现与被代理类组合。 应用实例 1、Windows 里面的快捷方式。 2、买火车票不一定在火车站买也可以去代售点。 3、spring aop。
优点 1、职责清晰。 2、高扩展性。 3、智能化。
缺点 1、由于在客户端和真实主题之间增加了代理对象因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作有些代理模式的实现非常复杂。
使用场景按职责来划分通常有以下使用场景 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护Protect or Access代理。 5、Cache代理。 6、防火墙Firewall代理。 7、同步化Synchronization代理。 8、智能引用Smart Reference代理。
注意事项 1、和适配器模式的区别适配器模式主要改变所考虑对象的接口而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别装饰器模式为了增强功能而代理模式是为了加以控制。
实现
public interface Image {void display();
}public class RealImage implements Image {private String fileName;public RealImage(String fileName){this.fileName fileName;loadFromDisk(fileName);}Overridepublic void display() {System.out.println(Displaying fileName);}private void loadFromDisk(String fileName){System.out.println(Loading fileName);}
}public class ProxyImage implements Image{private RealImage realImage;private String fileName;public ProxyImage(String fileName){this.fileName fileName;}Overridepublic void display() {if(realImage null){realImage new RealImage(fileName);}realImage.display();}
}public class ProxyPatternDemo {public static void main(String[] args) {Image image new ProxyImage(test_10mb.jpg);// 图像将从磁盘加载image.display(); System.out.println();// 图像不需要从磁盘加载image.display(); }
}对比
工厂模式定义一个创建对象的接口让其子类自己决定实例化哪一个工厂类工厂模式使其创建过程延迟到子类进行
ShapeFactory shapeFactory new ShapeFactory();
//获取 Circle 的对象并调用它的 draw 方法
Shape shape1 shapeFactory.getShape(CIRCLE);模板模式定义一个操作中的算法的骨架而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
Game game new Cricket();game new Football();game.play();策略模式定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换 Context context new Context(new OperationAdd());在模板模式中我们在父类规定处理的流程在子类实现具体处理。 如果我们将该模式用于生成实例它就演变为工厂模式。
一个“策略”是一个 整体的(完整的) 算法算法是可以被整体替换的。而模板方法只能被替换其中的特定点算法流程是固定不可变的