java知识总结-常见的几种设计模式

文章目录

  • 23种设计模式类型
    • 1.单例模式
    • 2.工厂设计模式
      • 2.1工厂方法模式
      • 2.2 抽象工厂模式
    • 3. 建造者模式
    • 4.策略模式
    • 5. 适配器模式
      • 5.1类适配器模式
      • 5.2对象适配器模式
      • 5.3接口适配器模式
    • 6.代理模式

23种设计模式类型

java中有23种设计模式
设计模式分为三种类型:

  • 创建型模式(5种):这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
    单例模式、抽象工厂模式、建造者模式、工厂方法模式、原型模式
  • 结构型模式(7种):关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
    适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  • 行为型模式(11种):这些设计模式特别关注对象之间的通信。
    模板方法模式、策略模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式

1.单例模式

单例模式是指一个类只允许产生一个实例化对象。也是最好理解的一种设计模式。
分为懒汉式和饿汉式。
应用场景:在我们的系统中,有些对象只需要一个,比如:线程池、缓存、对话框、注册表、日志对象、充当打印机,显卡等设备驱程序的对象。事实上,这类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的发生,比如程序的行为异常、资源使用过量、或者不一致的结果。
单例模式优点
- 对于使用频繁的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常客观的一笔系统开销。
- 由于new操作次数减少,因而对系统内存使用的频率也会降低,这将减轻GC压力,缩短GC停顿时间。

  • 饿汉式:构造方法私有化,外部无法产生新的实例化对象,只能通过static方法取得实例化对象。
    具体实现步骤:
    1)将采用单例设计模式的类的构造方法私有化(private修饰)。
    2)在其内部产生该类的实例化对象,并将其封装成private static类型。
    3)定义一个静态方法返回该类的实例。
//【线程安全,效率低】
package Test;
class Singleton {	
	private static Singleton instance=new Singleton(); //2.在内部类产生本类的实例化对象
	private Singleton() {	//1. 私有化构造方法
	}
	public static Singleton getInstance() { //3.通过静态方法返回instance对象
		return instance;
	}
	public void print() {
		System.out.println("Hello Singleton...");
	}
}
public class SingletonDemo{
	public static void main(String[] args) {
		Singleton singleton = null;//声明对象
		singleton=Singleton.getInstance();//取得实例化对象
		singleton.print();//调用方法			
	}	
}
  • 懒汉式:当第一次去使用Singleton对象的时候才会为其产生实例化对象的操作。
//【非线程安全,效率高】
package Test;
class Singleton {	
	private static Singleton instance;
	private Singleton() {	//1. 私有化构造方法
	}
	public static Singleton getInstance() { //2.通过静态方法返回instance对象
		if (instance==null) { // 3. 使用Singleton对象时才产生实例化对象操作
			instance = new Singleton();
		}
		return instance;
	}
	public void print() {
		System.out.println("Hello Singleton...");
	}
}
public class SingletonDemo{
	public static void main(String[] args) {
		Singleton singleton = null;//声明对象
		singleton=Singleton.getInstance();//取得实例化对象
		singleton.print();//调用方法			
	}	
}

但是上面这种懒汉式单例模式在多线程并发执行getInstance()的时候会出现线程不安全的问题。所以为了保证线程安全,又有两种写法:
方法1:在getInstance()加个synchronized锁就可以了。

//线程安全,效率低
class Singleton {	
	private static Singleton instance;
	private Singleton() {	//1. 私有化构造方法
	}
	public static synchronized Singleton getInstance() { //2.通过静态方法返回instance对象	
		if (instance==null) { // 3. 使用Singleton对象时才产生实例化对象操作
			instance = new Singleton();
		}
		return instance;
	}
	public void print() {
		System.out.println("Hello Singleton...");
	}
}

方法2:【推荐使用这种写法】

//【线程安全,效率高】
class Singleton {	
	private static Singleton instance;
	private Singleton() {	//1. 私有化构造方法
	}
	public static Singleton getInstance() { //2.通过静态方法返回instance对象		
		if (instance==null) { // 3. 使用Singleton对象时才产生实例化对象操作
			synchronized (Singleton.class) {
				if (instance==null) {
					instance = new Singleton();
				}
			}			
		}
		return instance;
	}
	public void print() {
		System.out.println("Hello Singleton...");
	}
}

推荐阅读:JAVA设计模式之单例模式

2.工厂设计模式

工厂模式分为工厂方法模式和抽象工厂模式。相当于程序在接口和子类之间加入一个过渡站(一个专门的类),通过这个过渡站可以动态获取实现了共同接口的子类实例化对象。【有一个专门的类来负责创建实例的过程】

2.1工厂方法模式

工厂方法模式又分3种:普通工厂方法模式、多个工厂方法模式、静态工厂方法模式

  1. 普通工厂方法模式:建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
package Test;
//发送邮件和短信的例子
interface Sender{
	void Send();
}
class MailSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Email Sending.....");	
	}	
}
class PhoneSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Phone Sending...");		
	}	
}
//过渡站,实例化对象
class SendFactory{
	public Sender produce(String className) {
		if ("Email".equals(className)) {
			return new MailSender();
		}else if("Phone".equals(className)) {
			return new PhoneSender();
		}else {
			System.out.println("className error...");
			return null;
		}
	}
}	
public class FactoryPattern{
	public static void main(String[] args) {
		SendFactory factory = new SendFactory();
		Sender sender = factory.produce("Email");
		sender.Send();
		sender = factory.produce("Phone");
		sender.Send();	
	}
}
  1. 多个工厂方法模式:是对普通工厂模式的改进,普通工厂方法模式中如果传递的字符串出错,则不能正确创建对象,多个工厂方法模式是提供多个工厂方法,分别创建对象。
package Test;
//发送邮件和短信的例子
interface Sender{
	void Send();
}
class MailSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Email Sending.....");	
	}	
}
class PhoneSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Phone Sending...");		
	}	
}
//过渡站,实例化对象
class SendFactory{
	public Sender ProduceMail() {
		return new MailSender();
	}
	public Sender producePhone() {
		return new PhoneSender();
	}
}
public class FactoryPattern{
	public static void main(String[] args) {
		SendFactory factory = new SendFactory();
		Sender sender = factory.ProduceMail();
		sender.Send();
		sender = factory.producePhone();
		sender.Send();
	}
}
  1. 静态工厂方法模式:将多个工厂方法模式的方法设置为静态的,不需要创建实例,直接调用即可。
package Test;
//发送邮件和短信的例子
interface Sender{
	void Send();
}
class MailSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Email Sending.....");	
	}	
}
class PhoneSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Phone Sending...");		
	}	
}
//过渡站,实例化对象
class SendFactory{
	public static Sender ProduceMail() {
		return new MailSender();
	}
	public static Sender producePhone() {
		return new PhoneSender();
	}
}
public class FactoryPattern{
	public static void main(String[] args) {
		Sender sender = SendFactory.ProduceMail();
		sender.Send();
		sender = SendFactory.producePhone();
		sender.Send();
	}
}

工厂方法模式的缺点:类的创建依赖工厂类,要想扩展程序,必须对工厂类进行修改。

2.2 抽象工厂模式

抽象工厂模式解决了工厂方法模式的缺点,抽象出来一个工厂接口,创建了多个工厂类,如果需要增加新功能,直接增加新工厂类即可。

package Test;
//发送邮件和短信的例子
interface Sender{
	void Send();
}
class MailSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Email Sending.....");	
	}	
}
class PhoneSender implements Sender{
	@Override
	public void Send() {
		System.out.println("Phone Sending...");		
	}	
}
//从工厂类中抽象出来一个接口
interface Producer{
	Sender produce();
}
//工厂类实例化mail对象
class SendMailFactory implements Producer{
	@Override
	public Sender produce() {
		return new MailSender();
	}	
}
//工厂类实例化phone对象
class SendPhoneFactory implements Producer{
	public Sender produce() {
		return new PhoneSender();
	}	
}
public class FactoryPattern{
	public static void main(String[] args) {
		Producer producer = new SendMailFactory();
		Sender sender = producer.produce();
		sender.Send();
		producer = new SendPhoneFactory();
		sender = producer.produce();
		sender.Send();
	}
}

3. 建造者模式

上面的工厂模式提供的是创建单个类的模式,建造者模式是将各种产品集中起来管理,用来创建复合对象(一个类有不同的属性)。
参考链接:设计模式知识笔记-建造者模式

4.策略模式

  1. 定义:策略模式定义了一系列算法,并将每个算法封装起来,而且使他们可以互相替换。
  2. 实现方式:
    1)提供公共接口或抽象类,定义需要使用的策略方法。
    2)多个实现的策略抽象类的实现类。
    3)环境类,对多个实现类的封装,提供接口类型的成员变量,可在客户端切换。
    4)客户端,调用环境类,进行不同策略的切换。

JDK中的TreeSet、TreeMap的排序功能使用类策略模式。

  1. 优点:
    1)提供了一系列可重用的算法族,恰当使用继承可把算法族公共代码放到父类,避免重复代码。
    2)可以在运行时切换对象内的算法。
    3)可以将算法的实现和使用算法的代码隔离开来。
    4)开闭原则。可以在不修改原代码的前提下,灵活增加新的算法。
    5)多重条件的语句不容易维护,策略模式可以避免使用多重语句(if-else)。

  2. 缺点:
    1)客户端必须知道所有的策略类,并决定使用哪一个策略类。这意味着客户端必须理解这些算法的区别,以便选择合适算法类。
    2)策略太多的话,会出现很多策略类。

  3. 主要角色:
    1)抽象策略类:定义了一个公共抽象接口,各种不同算法以不同方式实现这个接口,环境角色使用该接口调用不同算法。
    2)具体策略类:实现抽象策略定义的接口,提供具体的算法实现。
    3)上下文类(环境类):持有一个策略类的引用,最终给客户端调用。

  4. 模式结构:java知识总结-常见的几种设计模式_第1张图片

  5. 举例说明:

  • 角色:抽象决策类
package Test;
/**
* @author hehuan
* @date 2020年3月27日下午3:17:31
*/
public interface Strategy {
	public int doOperation(int num1,int num2);
}

-角色: 具体策略类

package Test;
/**
* @author hehuan
* @date 2020年3月27日下午3:19:33
*/
public class OperationAdd implements Strategy{
	@Override
	public int doOperation(int num1, int num2) {
			return num1+num2;
	}	
}
package Test;
/**
* @author hehuan
* @date 2020年3月27日下午3:20:17
*/
public class OperationSubstract implements Strategy{
	@Override
	public int doOperation(int num1, int num2) {
		return num1-num2;
	}
}
  • 角色:上下文类
package Test;
/**
* @author hehuan
* @date 2020年3月27日下午3:21:01
*/
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);
	}
}

测试:

package Test;
/**
* @author hehuan
* @date 2020年3月27日下午3:23:11
*/
public class StrategyPattern {
	public static void main(String[] args) {
		Context context = new Context(new OperationAdd());
		System.out.println("10+3="+context.executeStrategy(10, 3));
		context = new Context(new OperationSubstract());
		System.out.println("10-3="+context.executeStrategy(10, 3));
	}
}

结果:
java知识总结-常见的几种设计模式_第2张图片

5. 适配器模式

定义:适配器模式就是将某个类的接口转换成客户端所需要的另一个接口表示,使得接口不兼容的对象能够相互合作。就像我们平时使用的转换头、适配器之类的意思。
java知识总结-常见的几种设计模式_第3张图片
适配器模式主要分为三种:类适配器模式、对象适配器模式、接口适配器模式

5.1类适配器模式

原理:通过继承来实现适配器的功能。

举例说明:我有一台电脑带USB接口,我的U盘是TypeC接口的,我现在想要在电脑上插上我的U盘,但是现在接口不匹配,怎么办呢,我拿来了一个Typec转USB的转换器,在中间当个桥梁,这样我就可以将U盘和电脑进行连接交互了。

package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:27:44
*/
//描述接口USB的格式
public interface USB {
	void isUSB();
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:30:18
*/
//USB接口格式的具体实现
public class USBer implements USB{

	@Override
	public void isUSB() {
		System.out.println("插上USB接口");
		
	}

}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:28:17
*/
//描述接口TypeC的格式
public interface TypeC {
	void isTypeC();
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:31:18
*/
//这是个TypeC转USB的转换头
public class Adapter extends USBer implements TypeC{

	@Override
	public void isTypeC() {
		System.out.println("我是TypeC接口");
		isUSB();		
	}
}

测试:

package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:33:05
*/
public class AdapterPattern {
	public static void main(String[] args) {
		TypeC typeC = new Adapter();
		typeC.isTypeC();
	}
}

测试结果:
java知识总结-常见的几种设计模式_第4张图片

5.2对象适配器模式

原理:通过组合对象方式来实现适配器功能。
还是上面的例子,只是实现方式有些不同,Adapter不继承USBer类了,而是通过持有USB的实例对象来达到适配功能。

package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:31:18
*/
public class Adapter implements TypeC{
	private USB usb;
	public Adapter(USB usb) {
		this.usb=usb;
	}
	@Override
	public void isTypeC() {
		System.out.println("我是TypeC接口");	
		usb.isUSB();
	}
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:33:05
*/
public class AdapterPattern {
	public static void main(String[] args) {
		TypeC typeC = new Adapter(new USBer());
		typeC.isTypeC();
	}
}

5.3接口适配器模式

原理:通过抽象类来实现适配器功能。
当我们写一个接口,里面定义了很多抽象方法,但是现在我们可能只需要其中的一部分方法,如果实现该接口就必须对所有方法进行实现,这会导致代码浪费,为解决此问题,采用接口适配器的话,它采用一个抽象类作为中转,该抽象类实现来该接口的所有方法,我们如果想要实现接口的某个方法时,只需要对抽象类中的某个方法进行重写即可。【图画的不好看,能理解就好】
java知识总结-常见的几种设计模式_第5张图片

package Test;
/**
* @author hehuan
* @date 2020年3月28日上午10:35:32
*/
public interface Target {
	void USB();
	void Typec();
	void MiniDP();
	void HDMI();
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:31:18
*/

public class Adapter implements Target{

	@Override
	public void USB() {		
	}

	@Override
	public void Typec() {
	}

	@Override
	public void MiniDP() {
	}

	@Override
	public void HDMI() {
	}	
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:30:18
*/
public class Computer extends Adapter{
	public void USB() {
		System.out.println("转换成电脑USB接口");
	}
	public void HDMI() {
		System.out.println("转换成电脑HDMI接口");
	}
}
package Test;
/**
* @author hehuan
* @date 2020年3月28日上午10:48:12
*/
public class Phone extends Adapter{
	public void Typec() {
		System.out.println("转换成手机typec接口");
	}
}

测试:

package Test;
/**
* @author hehuan
* @date 2020年3月28日上午9:33:05
*/
public class AdapterPattern {

	public static void main(String[] args) {
		Computer computer = new Computer();
		computer.USB();
		computer.HDMI();
		Phone phone = new Phone();
		phone.Typec();
		
	}
}

结果:

转换成电脑USB接口
转换成电脑HDMI接口
转换成手机typec接口

总结:

  1. 当我们希望一个类转换为满足的另一新接口的类时,可创建一个新类继承原有类实现新接口,这种情况可采用类的适配器模式
  2. 当我们希望一个对象转换为满足另一个新接口对象时,可在新类中持有原类的一个实例,在新类方法中调用实例方法即可,这种情况采用对象的适配器模式
  3. 当我们接口中有很多方法,但又不想全部实现时,可以采用继承抽象类的方式来重写我们需要的方法,这种情况采用接口的适配器模式

6.代理模式

定义:代理模式是指给一个对象提供一个代理(中介),由代理控制对原对象的使用。代理分为静态代理动态代理。这里暂时讲静态代理。
优点

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。
  • 代理对象可以扩展目标对象的功能
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

缺点

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
  • 增加了系统的复杂度

角色:

  1. 抽象主题(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

举个例子:我想买个手机,我可以直接找手机厂商买,也可以通过手机店买,其中手机店相当于代理。

//抽象主题
package Test;
/**
* @author hehuan
* @date 2020年3月28日下午2:39:47
*/
//买手机接口
public interface BuyPhone {
	void buyphone();
}
//实际主题
package Test;
/**
* @author hehuan
* @date 2020年3月28日下午2:40:41
*/
public class Customer implements BuyPhone{

	@Override
	public void buyphone() {
		System.out.println("顾客买了一部手机");	
	}
}
//代理
package Test;
/**
* @author hehuan
* @date 2020年3月28日下午2:41:54
*/
public class PhoneProxy implements BuyPhone{
	private Customer customer;
	public PhoneProxy(Customer customer) {
		this.customer=customer;
	}
	@Override
	public void buyphone() {
		customer.buyphone();
		System.out.println("我是手机店代理,送顾客一张贴膜");		
	}
}

测试:

package Test;
/**
* @author hehuan
* @date 2020年3月28日下午2:43:43
*/
public class ProxyPattern {
	public static void main(String[] args) {
		Customer customer = new Customer();
		PhoneProxy phoneProxy = new PhoneProxy(customer);
		phoneProxy.buyphone();
	}
}

结果:

顾客买了一部手机
我是手机店代理,送顾客一张贴膜

上面的例子比较简单,好像看不出来代理的作用哈,这样说吧,代理不仅可以当中介卖你手机,他还能作出一些额外的工作,比如你买手机送你一张贴膜?比如给你手机设置权限你只能使用一部分功能?如果你手机出来问题直接找代理,不用去找厂商,厂商很忙的?

适用场景:
1)比如一张很大的图像,载入显示需要很长时间。
2)比如一个计算过程要花很久时间,但是需要显示中间结果。
3)一个对象只有某些访问权限,代理可以验证用户权限。

暂时先写到这,未完待续。。。

参考:
java几种常用设计模式简单示例
Java 中几种常用设计模式
23种设计模式汇总整理
Java设计模式之《适配器模式》及应用场景

发现了一个关于介绍设计模式的翻译网站,值得一看:Refactoring.Guru

你可能感兴趣的:(java)