java中有23种设计模式
设计模式分为三种类型:
单例模式是指一个类只允许产生一个实例化对象。也是最好理解的一种设计模式。
分为懒汉式和饿汉式。
应用场景:在我们的系统中,有些对象只需要一个,比如:线程池、缓存、对话框、注册表、日志对象、充当打印机,显卡等设备驱程序的对象。事实上,这类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的发生,比如程序的行为异常、资源使用过量、或者不一致的结果。
单例模式优点:
- 对于使用频繁的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常客观的一笔系统开销。
- 由于new操作次数减少,因而对系统内存使用的频率也会降低,这将减轻GC压力,缩短GC停顿时间。
//【线程安全,效率低】
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();//调用方法
}
}
//【非线程安全,效率高】
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设计模式之单例模式
工厂模式分为工厂方法模式和抽象工厂模式。相当于程序在接口和子类之间加入一个过渡站(一个专门的类),通过这个过渡站可以动态获取实现了共同接口的子类实例化对象。【有一个专门的类来负责创建实例的过程】
工厂方法模式又分3种:普通工厂方法模式、多个工厂方法模式、静态工厂方法模式
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();
}
}
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();
}
}
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();
}
}
工厂方法模式的缺点:类的创建依赖工厂类,要想扩展程序,必须对工厂类进行修改。
抽象工厂模式解决了工厂方法模式的缺点,抽象出来一个工厂接口,创建了多个工厂类,如果需要增加新功能,直接增加新工厂类即可。
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();
}
}
上面的工厂模式提供的是创建单个类的模式,建造者模式是将各种产品集中起来管理,用来创建复合对象(一个类有不同的属性)。
参考链接:设计模式知识笔记-建造者模式
JDK中的TreeSet、TreeMap的排序功能使用类策略模式。
优点:
1)提供了一系列可重用的算法族,恰当使用继承可把算法族公共代码放到父类,避免重复代码。
2)可以在运行时切换对象内的算法。
3)可以将算法的实现和使用算法的代码隔离开来。
4)开闭原则。可以在不修改原代码的前提下,灵活增加新的算法。
5)多重条件的语句不容易维护,策略模式可以避免使用多重语句(if-else)。
缺点:
1)客户端必须知道所有的策略类,并决定使用哪一个策略类。这意味着客户端必须理解这些算法的区别,以便选择合适算法类。
2)策略太多的话,会出现很多策略类。
主要角色:
1)抽象策略类:定义了一个公共抽象接口,各种不同算法以不同方式实现这个接口,环境角色使用该接口调用不同算法。
2)具体策略类:实现抽象策略定义的接口,提供具体的算法实现。
3)上下文类(环境类):持有一个策略类的引用,最终给客户端调用。
举例说明:
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));
}
}
定义:适配器模式就是将某个类的接口转换成客户端所需要的另一个接口表示,使得接口不兼容的对象能够相互合作。就像我们平时使用的转换头、适配器之类的意思。
适配器模式主要分为三种:类适配器模式、对象适配器模式、接口适配器模式
原理:通过继承来实现适配器的功能。
举例说明:我有一台电脑带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();
}
}
原理:通过组合对象方式来实现适配器功能。
还是上面的例子,只是实现方式有些不同,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();
}
}
原理:通过抽象类来实现适配器功能。
当我们写一个接口,里面定义了很多抽象方法,但是现在我们可能只需要其中的一部分方法,如果实现该接口就必须对所有方法进行实现,这会导致代码浪费,为解决此问题,采用接口适配器的话,它采用一个抽象类作为中转,该抽象类实现来该接口的所有方法,我们如果想要实现接口的某个方法时,只需要对抽象类中的某个方法进行重写即可。【图画的不好看,能理解就好】
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接口
总结:
- 当我们希望一个类转换为满足的另一新接口的类时,可创建一个新类继承原有类实现新接口,这种情况可采用类的适配器模式。
- 当我们希望一个对象转换为满足另一个新接口对象时,可在新类中持有原类的一个实例,在新类方法中调用实例方法即可,这种情况采用对象的适配器模式。
- 当我们接口中有很多方法,但又不想全部实现时,可以采用继承抽象类的方式来重写我们需要的方法,这种情况采用接口的适配器模式。
定义:代理模式是指给一个对象提供一个代理(中介),由代理控制对原对象的使用。代理分为静态代理和动态代理。这里暂时讲静态代理。
优点:
缺点:
角色:
举个例子:我想买个手机,我可以直接找手机厂商买,也可以通过手机店买,其中手机店相当于代理。
//抽象主题
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