文章目录
设计模式分类
单例模式
什么是单例模式
单例模式确定某一个类只有一个实例,而且自行实例化,并向整个系统提供这一个实例,在计算机系统中,线程池,缓存,日志对象,对话框,打印机,显卡的驱动程序对象常被设计为单例模式.
例如每台计算机可以有多个打印机,但只能有一个Printer
Spooler,以避免两个打印作业同时输出到打印机
单例模式的经典两类
1: 懒汉式 2: 饿汉式
懒汉式单例模式
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
singleton通过将构造方法限定为private避免了类在外部实例化,
在同一个虚拟机范围中, singleton只会通过getinstance方法访问.
但是,在多线程并发的情况下,其线程是不安全的,可以通过以下三种方式进行解决
1: 在getinstance方法加同步锁
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
2: 双重检查锁定
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
3: 静态内部类
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() { \`
return LazyHolder.INSTANCE;
}
}
这种方式是当被调用getInstance()时才去加载静态内部类LazyHolder,
LazyHolder在加载过程中会实例化一个静态的Singleton,
因为利用了classloader的机制来保证初始化instance时只有一个线程,
所以Singleton肯定只有一个,是线程安全的,
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
饿汉式单例模式
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
饿汉式和懒汉式的区别
1: 何时初始化单例
饿汉式是在类第一次加载的时候就将单例初始化完毕,保证getinstance的时候,单例已经存在
懒汉式是在getinstance的时候,在对单例进行初始化
2: 线程安全
饿汉式是线程安全的,可以直接用于多线程而不会出现问题
懒汉式不是线程安全的, 有三种方式来实现线程安全
3: 资源加载和性能
饿汉式,因为其在类加载的时候就已经初始化了一个单例对象,这样不管你是否调用了这个单例对象,这个单例对象所占的内存会一直存在,这样你
在第一次调用getinstance方法时,速度自然就时快的
懒汉式,是延迟加载的,在第一次调用getinstance方法时会去实例化一个单例对象,这样速度就会慢很多,但是当第一次调用结束后,就和饿汉式一样了
1: 加同步锁:
加同步锁的方式虽然解决了线程安全的问题,但是我们实际业务中并不是每次都要同步,会影响性能
2: 做两次null检查:
确保只有第一次调用单例的时候会做同步,这样线程是安全的,也同时减少了性能的损耗
3:
利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗
工厂模式
什么是工厂模式?
工厂模式是为创建对象提供过渡接口, 根据用户需求创建实例得设计模式
用户只需要提供材料,并不需要去关心其内部的具体实现
例子: 提一俩汽车,只需要去取车就可以,并不用关心这辆车是怎么去实现的
优点:
1: 一个调用者创建一个对象,只需要知道名称就可以了
2: 扩展性高,如果要增加一个产品,只需要增加一个工厂类就可以了
3: 屏蔽产品的具体实现,只需要关注产品的具体实现
工厂方法模式的分类
普通工厂模式 多个工厂方法模式 静态工厂方法模式
1: 普通工厂方法模式
建立一个工厂类,对实现了同一个接口的一个类进行实例的创建
public interface Sender {
public void Send();
}
//创建一个类实现sender接口
public class MailSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is mailsender!\");
}
}
//创建一个类实现sender接口
public class SmsSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is sms sender!\");
}
}
// 创建工厂实例
public class SendFactory {
public Sender produce(String type) {
if (\"mail\".equals(type)) {
return new MailSender();
} else if (\"sms\".equals(type)) {
return new SmsSender();
} else {
System.out.println(\"请输入正确的类型!\");
return null;
}
}
}
//创建工厂的测试类
public class FactoryTest {
//创建工厂实例对象 传递一个参数sms,会在工厂实例对象进行判断
//判断成立后会返回实现了同一个接口的类
public static void main(String\[\] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produce(\"sms\");
sender.Send();
}
}
2: 多个工厂方法模式
从上面例子中我们已经实现了一个简单工厂,但是会有一个问题,如果我们参数传递错了,就没有办法正确的创建类实例对象,而多个工厂发发模式,就是提供多个工厂方法,分别创建对象
public interface Sender {
public void Send();
}
//创建一个类实现sender接口
public class MailSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is mailsender!\");
}
}
//创建一个类实现sender接口
public class SmsSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is sms sender!\");
}
}
// 创建工厂实例
public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
//创建工厂的测试类
public class FactoryTest {
//在多个工厂模式下, 不会去通过传递参数的方式去进行调用,
而是直接调用工厂实例对象中的方法去实现,这样就会避免参数传递错误的情况发生
public static void main(String\[\] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
3: 静态工厂方法模式
在多个工厂方法模式上,将工厂实例对象中的所有方法变为静态的,
这样工厂测试类就不需要去创建实例,直接去调用即可
public interface Sender {
public void Send();
}
//创建一个类实现sender接口
public class MailSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is mailsender!\");
}
}
//创建一个类实现sender接口
public class SmsSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is sms sender!\");
}
}
// 创建工厂实例
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
//创建工厂的测试类
public class FactoryTest {
//在静态工厂模式下, 不会去通过传递参数的方式去进行调用,
而是直接调用工厂实例对象中的静态方法去实现,这样就会避免参数传递错误的情况发生
// 也更加省事
public static void main(String\[\] args) {
Sender sender = factory.produceMail();
sender.Send();
}
}
总结:
凡是出现大量产品需要创建并且都有共同的接口时,可以通过工厂模式进行创建
抽象工厂模式
什么是抽象工厂模式
工厂方法模式有一个缺点,就是程序的扩展性低,因为类的创建依赖与工厂类,也就是说如果要对程序进行扩展,那就必须修改工厂类,那这样显然是不好的,违反了闭包原则,我们可以通过抽象工厂模式进行解决,创建多个工厂类,这样一旦需要对原有的程序功能进行扩展,我们就只需要直接添加新的工厂类就可以了
//功能类实现接口
public interface Sender {
public void Send();
}
//工厂类实现接口
public interface Provider {
public Sender produce();
}
//里面是要实现的具体功能
public class MailSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is mailsender!\");
}
}
public class SmsSender implements Sender {
\@Override
public void Send() {
System.out.println(\"this is sms sender!\");
}
}
//工厂类实现接口,去实现具体的功能
public class SendMailFactory implements Provider {
\@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
\@Override
public Sender produce() {
return new SmsSender();
}
}
//测试类
public class Test {
public static void main(String\[\] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
总结:
如果想要实现一个新的功能,只需要再写一个功能实现类实现sender接口,
一个工厂类实现produce接口就可以了,不用去修改原有的代码
builder模式(建造者模式)
使用场景
当需要生产一台汽车的时候,我们需要,轮胎,发动机,座椅等配件,而且需要熟练的技术去进行组装,而建造者模式就是将组装过程与部件进行分离
概念
将一个复杂的构建与其表示分离,使得同样的构建过程可以创建不同的表示
与抽象工厂的区别: 在建造者模式中,
是由用户直接与指导者进行联系,指导者在联系建造者去完成最终的组装
[]{#TkKx-1631172300615 .anchor}
建造者模式的结构组成
Product: 表示被构造的复杂对象,其中包含需要构建的部件属性。
Builder: 创建一个产品对象的各个部件指定抽象接口。
ConcreteBuilder:
实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示。Director: 调用具体建造者角色以创建产品对象。
1.Product角色:组装一辆汽车首先的有各种配件,如发动机、轮胎、座椅等。
public class Car{
public String engine;
public String tyre;
public String seat;
public Car(){
}
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getTyre() {
return tyre;
}
public void setTyre(String tyre) {
this.tyre = tyre;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
2.Builder角色:知道了所需配件后,就需要生产配件了,定义一个生产配件的抽象建造者接口。
public interface Builder {
String buildEngine();
String buildTyre();
String buildSeat();
}
3.ConcreteBuilder角色:实现抽象的
建造者接口生成具体的建造者,并开始生产具体的配件。
public class CarBuilder implements Builder{
\@Override
public String buildEngine() {
// 生产发动机
return \"发动机\";
}
\@Override
public String buildTyre() {
// 生产轮胎
return \"轮胎\";
}
\@Override
public String buildSeat() {
// 生产座椅
return \"座椅\";
}
}
}
4.Director角色:在生产出配件后,由指导者指导组装配件生成汽车
public class CarDirector {
CarBuilder cb;
public CarDirector(CarBuilder cb){
this.cb=cb;
}
public Car constructCar(){
Car car=new Car();
car.setEngine(cb.buildEngine());
car.setTyre(cb.buildTyre());
car.setSeat(cb.buildSeat());
return car;
}
}
5.最终得到一辆汽车:
public class Client {
public static void main(String\[\] args) {
CarDirector carDirector=new CarDirector(new CarBuilder());
Car car=carDirector.constructCar();
System.out.println(car.getEngine()+car.getTyre()+car.getSeat());
}
}
MVC模式
为什么mvc模式不是23种设计模式的其中一种
GoF (Gang of Four,四人组, 《Design Patterns: Elements of Reusable
Object-Oriented Software》/《设计模式》一书的作者:Erich
Gamma、Richard Helm、Ralph Johnson、John
Vlissides)并没有把MVC提及为一种设计模式,而是把它当做"一组用于构建用户界面的类集合"。[]{#zqD3-1631173848879 .anchor}
在他们看来,它其实是其它三个经典的设计模式的演变:观察者模式(Observer)(Pub/Sub),
策略模式(Strategy)和组合模式(Composite)。根据MVC在框架中的实现不同可能还会用到工厂模式(Factory)和装饰器(Decorator)模式。
适配器模式
什么是适配器模式
把一个类接口转化成客户端所期待的另一个类的接口,使原来因接口不匹配而无法一起工作的类能够一起工作
适配器模式的优缺点
优点:
更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
透明、简单
客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单 & 更直接
更好的扩展性
在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
解耦性
将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码
符合开放-关闭原则
同一个适配器可以把适配者类和它的子类都适配到目标接口;可以为不同的目标接口实现不同的适配器,而不需要修改待适配
缺点:
过多的使用适配器,会让系统非常零乱,不易整体进行把握
适配器的使用场景
系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作,多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们
适配器模式的分类
类适配器, 对象适配器
类适配器:
优点:
使用方便,代码简化
仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例
缺点:
高耦合,灵活性低
使用对象继承的方式,是静态的定义方式
Target: 目标角色 两脚插头
Adaptee: 源角色 三脚插头
Adapter: 适配器 让三脚插头适配两脚插头
/\*\*
\* 目标角色,如举例中需要转换成的两脚插头
\* \@author 14501_000
\*/
public interface Target {
void handleReq();
}
/\*\*
\* 源角色,需要被适配的类,如举例中的三脚插头
\*/
public class Adaptee {
public void request(){
System.out.println( \"可以完成客户请求的需要的功能!\" );
}
}
/\*\*
\* 适配器,把源接口转换成目标接口,即将两脚插头转换为两脚插头
\*
\*/
public class Adapter extends Adaptee implements Target{
public void handleReq() {
super.request();
}
}
/\*\*
\* 客户端类,通过两脚插座进行工作
\*
\*/
public class Client {
public void work(Target t){
t.handleReq();
}
public static void main(String\[\] args){
Client c = new Client();
Target t = new Adapter();
c.work(t);
}
}
对象适配器
优点:
灵活性高、低耦合
采用 "对象组合"的方式,是动态组合方式
缺点:
使用复杂
需要引入对象实例
对象适配器模式不是使用继承模式,而是采用委派的方式
/\*\*
\* 目标角色,如举例中需要转换成的两脚插头
\*/
public interface Target {
void handleReq();
}
/\*\*
\* 源角色,需要被适配的类,如举例中的三脚插头
\*/
public class Adaptee {
public void request(){
System.out.println( \"可以完成客户请求的需要的功能!\" );
}
}
/\*\*
\* 适配器,把源接口转换成目标接口,即将三脚插头转换为两脚插头
\*
\*/
public class Adapter implements Target{
Adaptee adaptee ;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee ;
}
public void handleReq() {
adaptee.request();
}
}
public class Client {
public void work(Target t){
t.handleReq();
}
public static void main(String\[\] args ) {
Client c =new Client();
Adaptee adaptee =new Adaptee();
Target t = new Adapter(adaptee);
c.work( t );
}
}
观察者模式(发布 / 订阅模式)
什么是观察者模式
定义对象间的一种一对多的依赖关系;
当1个对象的状态发生改变时,所有依赖于它的对象都将得到通知 &
自动更新对应操作
观察者模式结构
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的
抽象方法。
具体主题(ConcreteSubject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(ConcreteObserver)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
package net.biancheng.c.observer;
import java.util.\*;
public class ObserverPattern {
public static void main(String\[\] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject {
protected List\<Observer\> observers = new ArrayList\<Observer\>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println(\"具体目标发生改变\...\");
System.out.println(\"\-\-\-\-\-\-\-\-\-\-\-\-\--\");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者
interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println(\"具体观察者1作出反应!\");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println(\"具体观察者2作出反应!\");
}
}