目录
开闭原则(OCP):一个软件实体(类、模块、函数)对扩展开放、对修改关闭
一、单例模式
保证一个类有且仅有一个实例
1 饿汉式单例
I 将构造器私有化,保证外部不能创建实例
II 类内部直接实例化对象(new)
III 私有化实例,向外界提供一个返回实例的接口(静态方法getSingleton())
class Singleton{
//类内部提供实例化对象
//static - 类加载时实例化对象,保证仅有一个实例
//final - 不允许更改其值
private static final Singleton singleton = new Singleton();
//构造方法私有化,类外部无法产生实例化对象
private Singleton(){}
//静态方法
public static Singleton getSingleton(){
return singleton;
}
}
优点:多个线程同时进入这个方法(getSingleton()),返回的也只是一个实例对象,因为这个对象在类加载时(每个类只加载一次)就已经创建完毕了。
缺点:由于在类加载时完成对象实例化,比较耗费资源
2 懒汉式单例
I 将构造器私有化,保证外部不能创建实例
II 类内部初始化一个实例(不创建对象,具体使用时创建)
III 向外界提供一个返回实例的接口(静态方法getSingleton())
public class Singleton{
//3.3 - volatile关键字保证执行到3.2时,前面的操作(3.1)全部完成,读在写后;保证不会指令重排
private static volatile Singleton singleton; 2-产生唯一的对象
private Singleton(){}
//1-构造方法私有化
//1.1 - 线程1 线程2 线程3 - 至少new三次
//3.2 - 线程2看见singleton!=null,所以返回,但此时线程1还未完成属性初始化,所以最终获得的对
//象不完整
public static Singleton getInstance(){
//3-提供返回对象的接口
if(singleton == null){
//第一次校验;验证确实是第一次调用
//2.1 - 线程1 线程2 线程3
synchronized(Singleton.class){
//1.2 - 加锁;任意一个时刻只有一个线程可以进来
if(singleton == null){
//2.3 - 第二次校验;不管进来多少线程,最终只会产生一个对象
//2.2 - 线程1进来,其他阻塞;但是线程1执行完,其他线程还会进来
//3.1 - 线程1
singleton = new Singleton();
}
}
}
return singleton;
}
}
(1)加锁:保证任意一个时刻只有一个线程可以进入创建singleton对象
加入同时有3个线程进入getInstance(),如果没有加锁,至少会产生三个singleton对象;加锁之后,可以保证同一时刻只有一个线程进入创建singleton对象
(2)加锁并不能保证Singleton类有且仅有一个实例对象
所以,产生双重校验
首先是第一次校验
第一次校验:判断singleton是否为空,验证确实是第一次调用singleton对象
此时,若同时有三个线程同时进入getInstance(),并且各自判断确定是第一次调用,虽然加锁可以保证同一时刻只有一个线程进入创建singleton对象,但是若线程1创建对象成功后,其他线程还是会进入并继续创建singleton对象
第二次校验:保证不管有多少线程进来,最终只会产生一个对象
(3)volatile关键字
创建singleton对象:singleton = new Singleton();
此代码不是原子操作,一共分为三个步骤实现
a.堆上开空间
b.singleton 指向堆空间 //完成这一步,表明singleton!=null
c.构造方法完成属性初始化
假如线程1执行到b步骤;此时线程2进入getInstance(),第一次校验时发现singleton != null,所以直接返回之前创建的singleton对象,但是事实上,该singleton对象并未完成属性初始化,最终获取的singleton对象并不完整,所以这种情况下,必然会出错;而volatile关键字规避了这种错误,它能够保证在线程2进行操作之前线程1已经完全执行完毕,达到读在写后,不会发生指令重排的目的
二、模板设计模式
概念:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
(1)定义了算法的步骤,对这些步骤的实现延迟到子类
(2)提供了一种代码复用的重要技巧
(3)抽象类可以定义具体方法、抽象方法和钩子
(4)抽象方法由子类实现
(5)为防止子类改变模板方法中的算法,可将模板方法声明为final
网上购物流程:浏览商品 — 选择商品 — 客服服务 — 快递服务 — 计算付款 — 查看商品
import java.util.Scanner;
/**
* 网上购物是一个抽象类
* 超类实现
*/
abstract class OnlineShopping{
/**
* 现在用同一个shopping()方法处理京东购物和淘宝购物
* 声明为final的原因是不希望子类覆盖这个方法
*/
public final void shopping(){
browseGoods(); //浏览商品
selectGoods(); //选择商品
//如果顾客想要服务我们才调用客服服务方法
if(customerwantsService()) {
customerService(); //客服服务
}
expressService(); //快递服务
calculatePrice(); //计算付款
viewGoods(); //查看商品
}
/**
* 京东和淘宝处理这些方法不同
* 因此这三个方法必须被声明为抽象,留给子类实现
*/
public abstract void customerService();
public abstract void expressService();
public abstract void calculatePrice();
public void browseGoods(){
System.out.println("浏览商品");
}
public void selectGoods(){
System.out.println("选择商品");
}
public void viewGoods(){
System.out.println("查看商品");
}
/**
* 钩子方法
* 超类中通常是默认实现
* 子类可以选择性的覆写此方法
* @return
*/
boolean customerwantsService(){
return true;
}
}
/**
* 子类实现
*/
class JD extends OnlineShopping{
public void customerService(){
System.out.println("京东客服服务");
}
public void expressService(){
System.out.println("京东快递服务");
}
public void calculatePrice(){
System.out.println("计算付款");
}
/**
* 子类覆写了钩子函数,实现自定义功能
* @return
*/
public boolean customerwantsService(){
String answer = getUserInput();
if(answer.equals("y")){
return true;
}else{
return false;
}
}
private String getUserInput(){
String answer = null;
System.out.println("您需要客服服务吗(y/n)?");
Scanner sc = new Scanner(System.in);
answer = sc.nextLine();
return answer;
}
}
/**
* 测试类
*/
class TaoBao extends OnlineShopping{
public void customerService(){
System.out.println("淘宝客服服务");
}
public void expressService(){
System.out.println("淘宝快递服务");
}
public void calculatePrice(){
System.out.println("计算付款");
}
}
public class newTest {
public static void main(String[] args) {
OnlineShopping jd = new JD();
OnlineShopping taobao = new TaoBao();
System.out.println("京东购物流程...\n");
jd.shopping();
System.out.println("\n*************\n");
System.out.println("淘宝购物流程...\n");
taobao.shopping();
}
}
三、工厂设计模式
1、简单工厂模式
概念:专门定义一个类用来创建其他类实例,被创建的实例通常都具有共同的父类
概要:一个抽象产品、多个具体产品、 一个工厂
优点:简单易于实现;将类的实例化交给工厂,易于解耦
缺点:添加产品时,需要修改工厂,违反了开闭原则
import java.util.Scanner;
/**
* 创建生产书的工厂,客户需要购买什么书,只要输入书的类型即可获取到
* 类的实例化由工厂进行,易于解耦
*/
//抽象产品:Book
interface Book{
void printBook();
}
//具体产品:chinesebook
class ChineseBook implements Book{
@Override
public void printBook() {
System.out.println("This is a ChineseBook");
}
}
//具体产品:englishbook
class EnglishBook implements Book{
@Override
public void printBook() {
System.out.println("This is a EnglishBook");
}
}
//工厂
class BookFactory{
public static Book getInstance(String type){
Book book = null;
if(type.equals("chinesebook")){
book = new ChineseBook();
}else if(type.equals("englishbook")){
book = new EnglishBook();
}
return book;
}
}
public class Client {
public static void buyBook(Book book){
book.printBook();
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("输入你想要的书籍类型...");
String type = in.nextLine();
Book book = BookFactory.getInstance(type);
buyBook(book);
}
}
2、工厂方法模式
概念:定义一个用来创建对象的接口,让子类决定实例化哪个类,让子类决定实例化延迟到子类
概要:一个抽象产品、多个具体产品、一个抽象工厂、多个具体工厂(每一个具体产品对应一个具体工厂)
优点:降低了代码的耦合度,对象的生成交给子类去完成;实现开闭原则(每次添加子产品,不需要修改原有代码)
缺点:增加了代码量,每个具体产品都需要一个具体工厂;需要增加新的抽象产品时(添加其他产品族),需要修改工厂,违背OCP
/**
* 针对每一个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象
*/
//抽象产品
interface Book{
void printBook();
}
//具体产品:chinesebook
class ChineseBook implements Book{
@Override
public void printBook() {
System.out.println("This is a ChineseBook");
}
}
//具体产品:englishbook
class EnglishBook implements Book{
@Override
public void printBook() {
System.out.println("This is an EnglishBook");
}
}
//抽象工厂
interface BookFactory{
Book1 createBook();
}
//具体工厂:ChinsesBookFactory(对应具体产品chinesebook)
class ChineseBookFactory implements BookFactory{
@Override
public Book createBook() {
return new ChineseBook();
}
}
//具体工厂:EnglishBookFactory(对应具体产品englishbook)
class EnglishBookFactory implements BookFactory{
@Override
public Book createBook() {
return new EnglishBook();
}
}
public class Client {
public static void buyBook(Book book){
book1.printBook();
}
public static void main(String[] args) {
BookFactory bookFactory = new ChineseBookFactory();
buyBook(bookFactory.createBook());
}
}
简单工厂模式与工厂方法模式的区别:
(1)简单工厂模式中,创建对象的逻辑判断放在了工厂类,客户不感知具体的类,但违背了开闭原则(如果要增加新的具体类,就必须修改工厂类)
(2)工厂方法模式中,通过扩展来新增具体类,符合开闭原则,但创建对象的逻辑判断放在了客户端,即用户必须感知到具体的工厂类
(3)工厂方法横向扩展很方便,假如该工厂又有新的产品MathBook要生产,那么只需要创建相应的工厂类和产品类去实现抽象工厂接口和抽象产品接口即可,而不用去修改原有已经存在的代码
3、抽象工厂模式
概念:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
概要:多个抽象产品、具体产品、抽象工厂(声明一组返回抽象产品的方法)、具体工厂(生成一组具体产品)
优点:代码解耦
实现多个产品族(相关联产品组成的家族),而工厂方法模式的单个产品,可以满足更多的生产需求
很好的满足OCP开放封闭原则
抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类 对于复杂对象的生产相当灵活易扩展
缺点:扩展产品族相当麻烦 而且扩展产品族会违反OCP,因为要修改所有的工厂
由于抽象工厂模式是工厂方法模式的扩展,总体的来说很笨重
/**
* 有两类产品:电脑、操作系统
*/
interface Computer{
void printComputer();
}
class MacbookComputer implements Computer{
@Override
public void printComputer() {
System.out.println("This is a macbook");
}
}
class SurfacebookComputer implements Computer{
@Override
public void printComputer() {
System.out.println("This is a surfacebook");
}
}
interface OperatingSystem{
void printSystem();
}
class MacOsSystem implements OperatingSystem{
@Override
public void printSystem() {
System.out.println("This is a mac os");
}
}
class Windows10System implements OperatingSystem{
@Override
public void printSystem() {
System.out.println("This is windows 10");
}
}
interface ProductionFactory{
Computer createComputer();
OperatingSystem createSystem();
}
class AppleFactory implements ProductionFactory{
@Override
public Computer createComputer() {
return new MacbookComputer();
}
@Override
public OperatingSystem createSystem() {
return new MacOsSystem();
}
}
class MsFactory implements ProductionFactory{
@Override
public Computer createComputer() {
return new SurfacebookComputer();
}
@Override
public OperatingSystem createSystem() {
return new MacOsSystem();
}
}
public class Client {
public static void buyComputer(Computer computer){
computer.printComputer();
}
public static void useSystem(OperatingSystem system){
system.printSystem();
}
public static void main(String[] args) {
ProductionFactory factory = new AppleFactory();
Computer computer = factory.createComputer();
OperatingSystem system = factory.createSystem();
buyComputer(computer);
useSystem(system);
}
}
总结:
简单工厂模式:
(1)对于同一等级结构的任一产品,若有新增加的产品,就需要修改已有代码
(2)工厂内有具体逻辑判断生成什么产品,将类的实例化交给工厂
工厂方法模式:简单工厂的扩展(工厂只生产一个产品)
(1)用来生产同一等级结构中的任一产品,对于新增加的产品支持
(2)实现类的逻辑判断交给客户端,添加新功能时只需要修改客户端和添加具体的功能,无需修改之前的类,要求客户端必须感知到具体的工厂类
抽象工厂模式:工厂方法模式的扩展(工厂生产两个或两个以上的产品)
(1)用来生产不同产品族的全部产品
(2)不止一个抽象产品,可添加产品族
(3)具体工厂可生成一组产品,而不仅仅生成单一产品
(4)抽象工厂可声明一组产品
四、代理设计模式
概念:两个子类共同实现一个接口,其中一个子类负责真实业务实现,另外一个子类完成辅助真实业务主题的操作
所有的真实业务操作都会有一个与之辅助的工具类(功能类)共同完成
interface ISubject{
void buyComputer(); //核心功能:买电脑
}
class RealSubject implements ISubject{
@Override
public void buyComputer() {
System.out.println("买一台联想电脑");
}
}
class ProxySubject implements ISubject{
private ISubject subject; //真正的操作业务
public ProxySubject(ISubject subject) {
this.subject = subject;
}
public void ProduceComputer(){
System.out.println("1.生产联想电脑");
}
public void afterSale(){
System.out.println("3.联想电脑售后服务");
}
@Override
public void buyComputer() {
this.ProduceComputer(); //真实操作前的准备
this.subject.buyComputer(); //调用真实业务
this.afterSale(); //操作后的首尾
}
}
class Factory{
public static ISubject getInstance(){
return new ProxySubject(new RealSubject());
}
}
public class Test {
public static void main(String[] args) {
ISubject subject = Factory.getInstance();
subject.buyComputer();
}
}