某类特定问题的代码设计解决方案,实际上是一套针对某类问题的代码设计经验总结。
在设计模式进行设计时需要遵循以下七个原则:
设计模式的类型总共分为:3大类、23种具体设计模式,具体如下:
针对三种设计模式类型,常见的设计模式是:
常见的是:单例、工厂方法(及 变式:工厂方法模式、抽象工厂模式)、建造者模式。
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (uniqueInstance == null)
,并且此时 uniqueInstance 为 null,那么会有多个线程执行 uniqueInstance = new Singleton();
语句,这将导致多次实例化 uniqueInstance。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
private static Singleton uniqueInstance = new Singleton();
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了多次实例化 uniqueInstance 的问题。
但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton();
这条语句,只是先后的问题,那么就会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
if (uniqueInstance == null) {
synchronized (Singleton.class) {
uniqueInstance = new Singleton();
}
}
uiqueInstance 采用 volatile 关键字修饰也是很有必要的。uniqueInstance = new Singleton();
这段代码其实是分为三步执行。
但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程下,有可能获得是一个还没有被初始化的实例,以致于程序出错。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()
方法从而触发 SingletonHolder.INSTANCE
时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。
这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举实现
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。
public enum Singleton {
uniqueInstance;
}
步骤1. 创建抽象产品类,定义具体产品的公共接口
abstract class Product{
public abstract void Show();
}
步骤2. 创建具体产品类(继承抽象产品类),定义生产的具体产品
//具体产品类A
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}
//具体产品类B
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生产出了产品C");
}
}
//具体产品类C
class ProductC extends Product{
@Override
public void Show() {
System.out.println("生产出了产品C");
}
}
步骤3. 创建工厂类,通过创建静态方法从而根据传入不同参数创建不同具体产品类的实例
class Factory {
public static Product Manufacture(String ProductName){
//工厂类里用switch语句控制生产哪种商品;
//使用者只需要调用工厂类的静态方法就可以实现产品类的实例化。
switch (ProductName){
case "A":
return new ProductA();
case "B":
return new ProductB();
case "C":
return new ProductC();
default:
return null;
}
}
}
步骤4. 外界通过调用工厂类的静态方法,传入不同参数从而创建不同具体产品类的实例
//工厂产品生产流程
public class SimpleFactoryPattern {
public static void main(String[] args){
Factory mFactory = new Factory();
//客户要产品A
try {
//调用工厂类的静态方法 & 传入不同参数从而创建产品实例
mFactory.Manufacture("A").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}
//客户要产品B
try {
mFactory.Manufacture("B").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}
//客户要产品C
try {
mFactory.Manufacture("C").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}
//客户要产品D
try {
mFactory.Manufacture("D").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}
}
}
生产出了产品A
生产出了产品C
生产出了产品C
没有这一类产品
步骤1: 创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
public abstract Product Manufacture();
}
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
abstract class Product{
public abstract void Show();
}
步骤3: 创建具体产品类(继承抽象产品类), 定义生产的具体产品;
//具体产品A类
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}
//具体产品B类
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生产出了产品B");
}
}
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
//工厂A类 - 生产A类产品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}
//工厂B类 - 生产B类产品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例
//生产工作流程
public class FactoryPattern {
public static void main(String[] args){
//客户要产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();
//客户要产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}
生产出了产品A
生产出了产品C
步骤1: 创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
public abstract Product ManufactureContainer();
public abstract Product ManufactureMould();
}
步骤2: 创建抽象产品族类 ,定义具体产品的公共接口;
abstract class AbstractProduct{
public abstract void Show();
}
步骤3: 创建抽象产品类 ,定义具体产品的公共接口;
//容器产品抽象类
abstract class ContainerProduct extends AbstractProduct{
@Override
public abstract void Show();
}
//模具产品抽象类
abstract class MouldProduct extends AbstractProduct{
@Override
public abstract void Show();
}
步骤4: 创建具体产品类(继承抽象产品类), 定义生产的具体产品;
//容器产品A类
class ContainerProductA extends ContainerProduct{
@Override
public void Show() {
System.out.println("生产出了容器产品A");
}
}
//容器产品B类
class ContainerProductB extends ContainerProduct{
@Override
public void Show() {
System.out.println("生产出了容器产品B");
}
}
//模具产品A类
class MouldProductA extends MouldProduct{
@Override
public void Show() {
System.out.println("生产出了模具产品A");
}
}
//模具产品B类
class MouldProductB extends MouldProduct{
@Override
public void Show() {
System.out.println("生产出了模具产品B");
}
}
步骤5:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
//A厂 - 生产模具+容器产品
class FactoryA extends Factory{
@Override
public Product ManufactureContainer() {
return new ContainerProductA();
}
@Override
public Product ManufactureMould() {
return new MouldProductA();
}
}
//B厂 - 生产模具+容器产品
class FactoryB extends Factory{
@Override
public Product ManufactureContainer() {
return new ContainerProductB();
}
@Override
public Product ManufactureMould() {
return new MouldProductB();
}
}
步骤6:客户端通过实例化具体的工厂类,并调用其创建不同目标产品的方法创建不同具体产品类的实例
//生产工作流程
public class AbstractFactoryPattern {
public static void main(String[] args){
FactoryA mFactoryA = new FactoryA();
FactoryB mFactoryB = new FactoryB();
//A厂当地客户需要容器产品A
mFactoryA.ManufactureContainer().Show();
//A厂当地客户需要模具产品A
mFactoryA.ManufactureMould().Show();
//B厂当地客户需要容器产品B
mFactoryB.ManufactureContainer().Show();
//B厂当地客户需要模具产品B
mFactoryB.ManufactureMould().Show();
}
}
生产出了容器产品A
生产出了容器产品B
生产出了模具产品A
生产出了模具产品B
从而:
步骤1:定义组装的过程(Builder):组装电脑的过程
public abstract class Builder {
//第一步:装CPU
//声明为抽象方法,具体由子类实现
public abstract void BuildCPU();
//第二步:装主板
//声明为抽象方法,具体由子类实现
public abstract void BuildMainboard();
//第三步:装硬盘
//声明为抽象方法,具体由子类实现
public abstract void BuildHD();
//返回产品的方法:获得组装好的电脑
public abstract Computer GetComputer();
}
步骤2: 电脑城老板委派任务给装机人员(Director)
public class Director {
//指挥装机人员组装电脑
public void Construct(Builder builder) {
builder.BuildCPU();
builder.BuildMainboard();
builder.BuildHD();
}
}
步骤3: 创建具体的建造者(ConcreteBuilder):装机人员
//装机人员1
public class ConcreteBuilder extend Builder{
//创建产品实例
Computer computer = new Computer();
//组装产品
@Override
public void BuildCPU(){
computer.Add("组装CPU")
}
@Override
public void BuildMainboard(){
computer.Add("组装主板")
}
@Override
public void BuildHD(){
computer.Add("组装主板")
}
//返回组装成功的电脑
@Override
public Computer GetComputer(){
return computer
}
}
步骤4: 定义具体产品类(Product):电脑
public class Computer{
//电脑组件的集合
private List parts = new ArrayList();
//用于将组件组装到电脑里
public void Add(String part){
parts.add(part);
}
public void Show(){
for (int i = 0;i
步骤5:客户端调用-小成到电脑城找老板买电脑
public class Builder Pattern{
public static void main(String[] args){
//逛了很久终于发现一家合适的电脑店
//找到该店的老板和装机人员
Director director = new Director();
Builder builder = new ConcreteBuilder();
//沟通需求后,老板叫装机人员去装电脑
director.Construct(builder);
//装完后,组装人员搬来组装好的电脑
Computer computer = builder.GetComputer();
//组装人员展示电脑给小成看
computer.Show();
}
}
组件CUP装好了
组件主板装好了
组件硬盘装好了
电脑组装完成,请验收
常见的是:适配器模式、代理模式、门面(外观)模式。
步骤1: 创建Target接口;
public interface Target {
//这是源类Adapteee没有的方法
public void Request();
}
步骤2: 创建源类(Adaptee) ;
public class Adaptee {
public void SpecificRequest(){
}
}
步骤3: 创建适配器类(Adapter)
class Adapter implements Target{
// 直接关联被适配类
private Adaptee adaptee;
// 可以通过构造函数传入具体需要适配的被适配类对象
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void Request() {
// 这里是使用委托的方式完成特殊功能
this.adaptee.SpecificRequest();
}
}
步骤4:定义具体使用目标类,并通过Adapter类调用所需要的方法从而实现目标
public class AdapterPattern {
public static void main(String[] args){
//需要先创建一个被适配类的对象作为参数
Target mAdapter = new Adapter(new Adaptee());
mAdapter.Request();
}
}
1.电器类
//灯类
public class SubSystemA_Light {
public void on(){
System.out.println("打开了灯....");
}
public void off(){
System.out.println("关闭了灯....");
}
}
//电视类
public class SubSystemB_Television {
public void on(){
System.out.println("打开了电视....");
}
public void off(){
System.out.println("关闭了电视....");
}
}
//空调类
public class SubSystemC_Aircondition {
public void on(){
System.out.println("打开了电视....");
}
public void off(){
System.out.println("关闭了电视....");
}
}
2.客户端调用:小成爷爷使用电器情况
public class Facade Pattern{
public static void main(String[] args){
{
SubSystemA_Light light = new SubSystemA_Light();
SubSystemB_Television television = new SubSystemB_Television();
SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();
//起床后开电器
System.out.prinln("起床了");
light.on();
television.on();
aircondition.on();
System.out.prinln("可以看电视了");
//睡觉时关电器
System.out.prinln("睡觉了");
light.off();
television.off();
aircondition.off();
System.out.prinln("可以睡觉了");
}
}
结果:
起床了
打开了灯
打开了电视
打开了空调
可以看电视了
睡觉了
关闭了灯
关闭了电视
关闭了空调
可以睡觉了
从上面可以看出,在不使用外观模式的情况下,小成爷爷需要对每个电器都进行操作,非常不方便
客户端与三个子系统都发生了耦合,使得客户端程序依赖与子系统。
小成买了一个智能家具控制器(外观对象/统一接口)给他爷爷,他爷爷只需要一键就能打开/关闭 灯、电视机、空调。
3.外观类:智能遥控器
public class Facade{
SubSystemA_Light light;
SubSystemB_Television television ;
SubSystemC_Aircondition aircondition;
//传参
public Facade(SubSystemA_Light light,SubSystemB_Television television,SubSystemC_Aircondition aircondition){
this.light = light;
this.television = television ;
this.aircondition =aircondition;
}
//起床后一键开电器
public void on{
System.out.prinln("起床了");
light.on();
television.on();
aircondition.on();
}
//睡觉时一键关电器
System.out.prinln("睡觉了");
light.off();
television.off();
aircondition.off();
}
}
4.客户端调用:爷爷使用智能遥控器的时候
public class Facade Pattern{
public static void main(String[] args){
{
//实例化电器类
SubSystemA_Light light = new SubSystemA_Light();
SubSystemB_Television television = new SubSystemB_Television();
SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();
//传参
Facade facade = new Facade(light,television,aircondition);
//客户端直接与外观对象进行交互
facade.on;
System.out.prinln("可以看电视了");
facade.off;
System.out.prinln("可以睡觉了");
}}
步骤1: 创建抽象对象接口(Subject):声明你(真实对象)需要让代购(代理对象)帮忙做的事(买Mac)
public interface Subject {
public void buyMac();
}
步骤2: 创建真实对象类(RealSubject),即”我“
public class RealSubject implement Subject{
@Override
public void buyMac() {
System.out.println(”买一台Mac“);
}
}
步骤3:创建代理对象类(Proxy),即”代购“,并通过代理类创建真实对象实例并访问其方法
public class Proxy implements Subject{
@Override
public void buyMac{
//引用并创建真实对象实例,即”我“
RealSubject realSubject = new RealSubject();
//调用真实对象的方法,进行代理购买Mac
realSubject.buyMac();
//代理对象额外做的操作
this.WrapMac();
}
public void WrapMac(){
System.out.println(”用盒子包装好Mac“);
}
}
步骤4:客户端调用
public class ProxyPattern {
public static void main(String[] args){
Subject proxy = new Proxy();
proxy.buyMac();
}
}
买一台Mac
用盒子包装好Mac
常见的是:策略模式、观察者模式 和模板方法模式。
将算法的责任和本身进行解耦,使得:
步骤1: 定义抽象策略角色(Strategy):百货公司所有促销活动的共同接口
public abstract class Strategy {
public abstract void Show();
}
步骤2:定义具体策略角色(Concrete Strategy):每个节日具体的促销活动
//为春节准备的促销活动A
class StrategyA extends Strategy{
@Override
public void show() {
System.out.println("为春节准备的促销活动A");
}
}
//为中秋节准备的促销活动B
class StrategyB extends Strategy{
@Override
public void show() {
System.out.println("为中秋节准备的促销活动B");
}
}
//为圣诞节准备的促销活动C
class StrategyC extends Strategy{
@Override
public void show() {
System.out.println("为圣诞节准备的促销活动C");
}
}
步骤3:定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员
class Context_SalesMan{
//持有抽象策略角色的引用
private Strategy strategy;
//生成销售员实例时告诉销售员什么节日(构造方法)
//使得让销售员根据传入的参数(节日)选择促销活动(这里使用一个简单的工厂模式)
public SalesMan(String festival) {
switch ( festival) {
//春节就使用春节促销活动
case "A":
strategy = new StrategyA();
break;
//中秋节就使用中秋节促销活动
case "B":
strategy = new StrategyB();
break;
//圣诞节就使用圣诞节促销活动
case "C":
strategy = new StrategyC();
break;
}
}
//向客户展示促销活动
public void SalesManShow(){
strategy.show();
}
}
步骤4:客户端调用-让销售员进行促销活动的落地
public class StrategyPattern{
public static void main(String[] args){
Context_SalesMan mSalesMan ;
//春节来了,使用春节促销活动
System.out.println("对于春节:");
mSalesMan = Context_SalesMan SalesMan("A");
mSalesMan.SalesManShow();
//中秋节来了,使用中秋节促销活动
System.out.println("对于中秋节:");
mSalesMan = Context_SalesMan SalesMan("B");
mSalesMan.SalesManShow();
//圣诞节来了,使用圣诞节促销活动
System.out.println("对于圣诞节:");
mSalesMan = Context_SalesMan SalesMan("C");
mSalesMan.SalesManShow();
}
}
对于春节:
为春节准备的促销活动A
对于中秋节:
为中秋节准备的促销活动B
对于圣诞节:
为圣诞节准备的促销活动B
在对象之间定义一对多的依赖, 这样一来, 当一个对象改变状态, 依赖它的对象都会收到通知, 并自动更新
步骤1:定义观察者接口
public interface Observer {
void response();
}
步骤2:定义被观察者接口
public abstract class Subject {
private Vector observerList = new Vector<>();
public void attachObserver(Observer observer) {
observerList.add(observer);
}
public void detachObserver(Observer observer){
observerList.remove(observer);
}
public void notifyObservers(){
for (Observer observer: observerList){
observer.chaoCai();
}
}
}
步骤3:定义具体的被观察者
public class ZhuChu extends Subject{
public void go(){
System.out.println("主厨开始炒菜");
notifyObservers();
}
}
步骤4:定义具体的观察者
public class DaoYou implements Observer{
@Override
public void chaoCai() {
System.out.println("倒油");
}
}
public class JiaYan implements Observer{
@Override
public void chaoCai() {
System.out.println("加盐");
}
}
public class LiaoJiu implements Observer{
@Override
public void chaoCai() {
System.out.println("加料酒");
}
}
步骤4:定义客户端
public class Client {
public static void main(String[] args) {
ZhuChu zhuChu = new ZhuChu();
DaoYou daoYou = new DaoYou();
JiaYan jiaYan = new JiaYan();
LiaoJiu liaoJiu = new LiaoJiu();
zhuChu.attachObserver(daoYou);
zhuChu.attachObserver(jiaYan);
zhuChu.attachObserver(liaoJiu);
zhuChu.go();
}
}
主厨开始炒菜
倒油
加盐
加料酒
步骤1:创建抽象模板结构(Abstract Class):炒菜的步骤
public abstract class Abstract Class {
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void cookProcess(){
//第一步:倒油
this.pourOil();
//第二步:热油
this.HeatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
//第一步:倒油是一样的,所以直接实现
void pourOil(){
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
void HeatOil(){
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现
abstract void pourVegetable();
//第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现
abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
void fry();{
System.out.println("炒啊炒啊炒到熟啊");
}
}
步骤2:创建具体模板(Concrete Class),即”手撕包菜“和”蒜蓉炒菜心“的具体步骤
//炒手撕包菜的类
public class ConcreteClass_BaoCai extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下锅的蔬菜是包菜“);
}
@Override
public void pourSauce(){
System.out.println(”下锅的酱料是辣椒“);
}
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下锅的蔬菜是菜心“);
}
@Override
public void pourSauce(){
System.out.println(”下锅的酱料是蒜蓉“);
}
}
步骤3:客户端调用-炒菜了
public class Template Method{
public static void main(String[] args){
//炒 - 手撕包菜
ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
BaoCai.cookProcess();
//炒 - 蒜蓉菜心
ConcreteClass_ CaiXin = new ConcreteClass_CaiXin();
CaiXin.cookProcess();
}
}
倒油
热油
下锅的蔬菜是包菜
下锅的酱料是辣椒
炒啊炒啊炒到熟
倒油
热油
下锅的蔬菜是菜心
下锅的酱料是蒜蓉
炒啊炒啊炒到熟
感兴趣的可以借鉴:
常用设计模式汇总,告诉你如何学习设计模式