设计模式
什么是设计模式呢?
设计模式是在大量的实践中总结和理论化的优选的代码结构、编程风格、以及解决问题的思考方式。
作为入门 下面介绍三种家喻户晓的设计模式 单例模式 工厂模式 代理模式
单例模式
是什么?
对于某个类中仅存在一个对象实例,并且该类会提供一个获取该实例的静态方法 便是单例模式
应用场景
解决数据的共享问题 当很多用户需要操作同一个数据时我为了保证数据的同步我们一般使用单例模式解决
比如:
☼ 网站的计数器,一般也是采用单例模式实现,否则难以同步。
☼多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程。
☼应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加行控制。
☼Spring 框架中bean默认就是单例模式。
满足条件
单例类中只能有一个实例
单例类中必须自己创建自己的唯一对象实例
单例类中需要具有对外提供该唯一对象实例的方法
一般创建步骤
第一步 构造方法私有化 (使该类不能被实例化)
第二步 声明当前类对象 直接实例化(饿汉式)再静态方法中实例化(懒汉式)
第三步 声明一个静态方法 用于传递实例化对象
示例
编写一个单例类 (饿汉式)
public class SignTon {
// 第一步 构造方法私有化 (使该类不能被实例化)
// 第二步 声明当前类对象 直接实例化(饿汉式)再静态方法中实例化(懒汉式)
// 第三步 声明一个静态方法 用于传递实例化对象
private static final SignTon st = new SignTon();//饿汉式
//构造方法私有化 该类不可被实例化
private SignTon() {
}
//编写静态方法用于返回实例化对象 确保该类只有一个对象能操作该类的属性和方法
public static SignTon getInstance() {
return st;
}
}
编写一个单例类的测试类 所有声明的所有对象访问的都是同一个唯一对象实例
public class SignTonTest {
public static void main(String[] args) {
//获取对象 并赋值给stA stB
SignTon stA = SignTon.getInstance();
SignTon stB = SignTon.getInstance();
//stA stB两个对象的引用其实都是一个对象
System.out.println(stA==stB);
}
}
返回值为true 说明唯一访问
下面是使用懒汉式实现单例类
public class SignTonB {
//懒汉式
private static SignTonB st= null;
private SignTonB() {
}
//此时静态方法要加入判断 判断对象是否已经被创建
public static SignTonB getInstance() {
if(st==null) {
st = new SignTonB();
}
return st;
}
}
饿汉式和懒汉式的区别
✤饿汉就是类一旦加载,就把单例初始化完成,保证返回唯一实例getInstance的时候,单例是已经存在的了。
✤而懒汉比较懒,只有当调用返回实例方法是才会实例化唯一的对象,然后返回该唯一实例。
✤单从资源利用效率角度来讲(饿汉比懒汉式单例类稍差些),但是从速度和反应时间角度来讲,饿汉比懒汉式单例类稍好
注意
懒汉式存在线程安全问题,可以使用多线程修复
工厂模式
是什么?
工厂模式(Factory Pattern)是Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
怎么个意思?
人们需要物品—>去找工厂(买什么物品返回什么对象)—>工厂开始制造物品(种类繁多 但是一个接口代表一个类 子类代表具体的商品)
示例
工厂模式应用了面向对象的多态中的向上转型 没有知识积累的可以参考文章中的多态部分
Java面向对象
你要买宠物 去宠物店买 买什么给什么(单一种类)
创建宠物接口 所有具体宠物都是宠物类
public interface Pet {
//吃
void eat();
}
创建具体的宠物狗和猫 具体宠物(物品)
public class Dog implements Pet {
@Override
public void eat() {
// TODO 自动生成的方法存根
System.out.println("狗吃骨头!");
}
}
public class Cat implements Pet{
@Override
public void eat() {
// TODO 自动生成的方法存根
System.out.println("猫吃鱼!");
}
}
宠物商店提供宠物(工厂类) 要什么给什么 向上转型
public class PetShop {
//售卖方法 向上转型
public Pet getPet(String name) {
if(name.equals("狗")) {
return new Dog();
}else if(name.equals("猫")) {
return new Cat();
}
return null;
}
}
你要买一只狗
public class BuyPet {
public static void main(String[] args) {
//商店提供宠物
PetShop petShop = new PetShop();
//要买什么宠物 返回什么宠物
Pet pet = petShop.getPet("狗");
//要买狗就得到了狗
pet.eat();
}
}
你不许要知道狗是怎么来的就得到了一只狗
工厂模式小结
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例:
1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
2、Hibernate 换数据库只需换方言和驱动就可以。
优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
代理模式
概述:为其他对象提供一种代理以控制对这个对象的访问。
示例
张三想吃蛋炒饭 但是因为各种原因不能自己做饭 所以让王五去做蛋炒饭 即王五做的蛋炒饭张三吃
编写做饭接口 需要被代理事情的类型
public interface Food {
public void Cook();
}
编写需要被代理的具体事物 做蛋炒饭
public class EggFriedRice implements Food{
@Override
public void Cook() {
// TODO 自动生成的方法存根
System.out.println("这是需要做的蛋炒饭");
}
}
王五做蛋炒饭 谁来代理(王五)—> 代理什么(做饭) ---->做什么饭(蛋炒饭)
public class WangWu implements Food{
Food food;
@Override
public void Cook() {
// TODO 自动生成的方法存根
food.Cook();
}
public WangWu() {
System.out.println("王五在做蛋炒饭");
//做蛋炒饭
food = new EggFriedRice();
}
}
张三让王五做蛋炒饭
public class ZhangSan {
public static void main(String[] args) {
Food eggF = new WangWu();
eggF.Cook();
}
}
代理模式小结
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
应用场景:
1、Windows 里面的快捷方式。
2、spring框架aop面向切面思想。
优缺点:
优点: 1、职责清晰。2、高扩展性。3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。