设计模式
1、常见的设计模式
我们常见的设计模式有工厂模式、代理模式、单例模式、策略模式、观察者模式、包装设计模式、模板方法模式等等。
我们在项目代码编写中手写代码用的比较多,一般就模板方法模式、责任链模式,策略模式、单例模式这些,像工厂模式、代理模式这一类模式手写的代码反而不多。
这一类手写的不多但是大部分我们使用的Spring框架情况下对其还是能够了解,像我们使用的打印日志方法LoggerFactory,还有我们的时间方法DateFormat在我们的框架底层已经是给我们设计好了的经常直接拿来使用的
1.1 单例模式
说到设计模式不得不提三大类中的创建型模式,该类模式下重点关注一个创建,创建什么?创建对象、创建实现过程,它的主要特点是将对象的创建与使用分离,这样在一定程度上降低系统的耦合,方便对项目进行拓展和维护。
而我们的单例模式,在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。
单例模式有以下3个特点:
1)单例类只有一个对象;
2)该单例对象必须由单例类自行创建;
3)单例类对外提供一个访问该单例的全局访问店。
单例模式下分:饿汉式、简单懒汉式(在方法申明时加锁)、DCL双重检验加锁(进阶懒汉式)、静态内部类(优雅懒汉式)、枚举等等。
无论是哪一种,我们都大致分两大类:饿汉式、懒汉式
所谓的饿汉式指还没有被用到就提前直接初始化了对象。
所谓的懒汉式指当需要用到的时候才进行初始化。
/**
* 饿汉买烟
* @author HYF
* 1、该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
* 2、饿汉式单例在类 创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,以后线程安全
* 的,可以直接用于多线程而不会出现问题。
*/
public class HungryCigarette {
private static final HungryCigarette instance = new HungryCigarette();
private HungryCigarette(){}
public static HungryCigarette getInstance(){
return instance;
}
}
/**
* 懒汉买烟
* @author HYF
* 1、该模式的特点是类加载时没有生成单例,只有当第一次调用 getInstance 方法才去创建单例。
* 2、如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,
* 否则将存在线程非安全的问题。如果不删除这两个关键字就保证线程安全,但是每次访问时都要同步,
* 会影响性能,且小号更多的资源,这是懒汉式单例的缺点。
*/
public class LazyCigarette {
//保证instance在所有线程中同步
private static volatile LazyCigarette instance = null;
//私有的,避免在外部被实例化
private LazyCigarette(){}
//getInstance 方法前同步
public static synchronized LazyCigarette getInstance(){
if (instance == null){
instance = new LazyCigarette();
}
return instance;
}
}
/**
* DCL懒汉买烟
* @author HYF
* 方法加锁
*/
public class HungryCigaretteEasy {
//这里一定要volatile修饰防止指令重排导致线程不安全的问题
private static volatile HungryCigaretteEasy hungryCigaretteEasy = null;
private HungryCigaretteEasy(){
}
public static synchronized HungryCigaretteEasy getInstance(){
if (hungryCigaretteEasy == null){
synchronized (HungryCigaretteEasy.class){
if (hungryCigaretteEasy == null){
hungryCigaretteEasy = new HungryCigaretteEasy();
}
}
}
return hungryCigaretteEasy;
}
}
/**
* @auther HYF
* 静态内部类 懒汉买烟
*/
public class HungryCigaretteStatic {
private HungryCigaretteStatic(){}
//使用静态内部类的方法来实现懒汉式加载,保证一定的线程安全
private static class LazyHolder{
private static final HungryCigaretteStatic hungryCigaretteStatic = new HungryCigaretteStatic();
}
public static final HungryCigaretteStatic getInstance(){
return LazyHolder.hungryCigaretteStatic;
}
}
上面举例了四种比较常见的懒汉式、饿汉式的方法,在我们的实际运用中(尤其是在项目中)使用静态内部类的方式实现单例的情况占多数,当然这只是在对于纯手写的情况下,也就是没有Spring的环境下,这样会让代码看起来简洁易读。我们提到了在Spring的环境下,那么在有Spring的环境下的情况的时候,直接把这些操作交给框架已有的容器管理可能会更加方便,因为Spring中默认的就是单例。
1.2 工厂方法模式
Factory Method是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类。通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化
说到工厂模式,那我们就不得不再提及简单工厂,和抽象工厂。这三种我个人人为可以都归纳为工厂模式,且是该模式下的三种特例,那这三者都是工厂模式也一定会有某种关系。我愿意这样来描述:
简单工厂——>工厂——>抽象工厂
一环套一环,由浅到深。话不多说,上代码:
假设我和老王要去买烟,我觉得格调好,他觉得玉溪好
先从简单工厂开始
package meeting.Factoryeaample;
/**
* @author HYF
* 买烟买烟先定义烟种类的接口
* Simple Factory模式
*/
public interface Cigarette {
void good();//烟好
void bad();//烟不好
}
/**
*格调烟
**/
public class GeDiao implements Cigarette {
public void good(){
System.out.println("格调天下第一");
}
public void bad(){
System.out.println("玉溪天下第二");
}
}
public class YuXi implements Cigarette {
public void good(){
System.out.println("软玉是好烟");
}
public void bad(){
System.out.println("硬玉是坏烟");
}
}
public class BuyCigarette {
// DateFormat;
/**
* 简单工厂方法
*/
public static Cigarette buyCigarette(String witch){
if (witch.equalsIgnoreCase("GeDiao")){//如果是格调则返回格调实例
return new GeDiao();
}else if (witch.equalsIgnoreCase("YuXi")){//如果是玉溪则返回玉溪实例
return new YuXi();
}else{
return null;
}
}
}
/**
* @author HYF
*/
public class BuyCigaretteTest {
/**
* 1、该种模式下,我要买烟,只需要像卖烟工厂(BuyCigarette)发送请求我要买什么烟,
* 而卖烟工厂(BuyCigarette)在收到请求后会自动判断创建和提供哪一种烟
* 2、随之而来的问题就是当我想要买华子的时候,工厂里没有!!!卖烟就必须得知道客户可能会买什么烟
* 要知道怎么创建,以及如何卖给我,没有的话就只能修改工厂去添加烟的种类,因此该种模式开放性较差。
* @param args
*/
public static void main(String args[]){
/**
* 开始买烟的过程
*/
BuyCigarette buyCigarette = new BuyCigarette();
buyCigarette.buyCigarette("GeDiao").good();
}
}
接下来是工厂模式
public interface BuyCigarette2 {
/**
* 工厂方法
*/
public Cigarette buyCigarette2();//定义买烟这一过程
}
/**
* BuyGeDiao是对BuyCigarette2接口的实现
*/
public class BuyGeDiao implements BuyCigarette2 {
public Cigarette buyCigarette2() {
return new GeDiao();
}
}
/**
* BuyYuXi是对BuyCigarette2接口的实现
*/
public class BuyYuXi implements BuyCigarette2 {
public Cigarette buyCigarette2(){
return new YuXi();
}
}
/**
* @author HYF
*/
public class BuyCigarette2Test {
/**
* 工厂模式
* A:工厂方法模式和简单工厂模式在结构上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,
* 而简单工厂模式把核心放在一个具体类上。工厂方法模式可 以允许很多具体工厂类从抽象工厂类中将
* 创建行为继承下来,从而可以成为多个简单工厂模式的综合,进而推广了简单工厂模式。
* B:工厂方法模式退化后可以变得很像简单工厂模式。设想如果非常确定一个系统只需要一个具体工厂
* 类,那么就不妨把抽象工厂类合并到具体的工厂类中去。由于 反正只有一个具体工厂类,所以不妨将
* 工厂方法改成为静态方法,这时候就得到了简单工厂模式。
* C:如果需要加入一个新的烟,那么只需要加入一个新的烟类 以及它所对应的工厂类。没有必要修改
* 客户端,也没有必要修改抽象工厂角色或者其他已有的具体工厂角色。对于增加新的烟种类而言,这个系统完全支持“开-闭 ”原则。
* 但是随之而来的是随着种类的增多类的数量将会成倍的增加,使得系统越来越复杂,增加系统压力
* @param args
*/
public static void main(String args[]){
//我要买格调
BuyGeDiao buyGeDiao = new BuyGeDiao();
buyGeDiao.buyCigarette2().good();
//我要买玉溪
BuyYuXi buyYuXi = new BuyYuXi();
buyYuXi.buyCigarette2().good();
}
}
最后是我们的抽象工厂
我们买烟也买完了,那么如果我突然想了解下生产烟的外包壳和烟草是怎么一回事。
/**
* @author HYF
* 抽象工厂方法下的工厂
*/
public interface BuyCigarette3 {
//制造外壳
public Enclosure createEnclosure();
//包装烟草
public Tobacco createTobacco();
}
/**
* @author HYF
* 生产烟外壳
*/
public interface Enclosure {
}
/**
* 硬外壳
*/
public class EnclosureA implements Enclosure {
public EnclosureA(){
System.out.println("制造用的硬壳烟");
}
}
/**
* 软外壳
*/
public class EnclosureB implements Enclosure {
public EnclosureB(){
System.out.println("制造软外壳");
}
}
/**
* 烟草
*/
public interface Tobacco {
}
/**
* 一等烟草
*/
public class TobaccoA implements Tobacco {
public TobaccoA(){
System.out.println("用的一等烟草");
}
}
/**
* 二等烟草
*/
public class TobaccoB implements Tobacco {
public TobaccoB(){
System.out.println("用的二等烟草");
}
}
**
* @author HYF
* 抽象工厂模式下的买烟
* 当每个抽象产品都有多于一个的具体子类的时候(外包装有型号A和B两种,烟草也有型号A和B两种)
* 工厂角色怎么知道实例化哪一个子类呢?抽象工厂模式提供两个具体工厂角色(格调生产工厂和玉溪生产工厂),
* 分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化,每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。
*/
/**
* 优点:一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建);
* 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要修改工厂抽象类里加代码,又修改具体的实现类里面加代码;
* 增加了系统的抽象性和理解难度;
*/
public class BuyCigaretteTest3 {
public static void main(String[] args) {
//生产一包格调所需材料
BuyGeDiao2 buyGeDiao2 = new BuyGeDiao2();
buyGeDiao2.createEnclosure();
buyGeDiao2.createTobacco();
//生产一包玉溪所需材料
BuyYuXi2 buyYuXi2 = new BuyYuXi2();
buyYuXi2.createEnclosure();
buyYuXi2.createTobacco();
}
}
以上就是我们为了买包烟而引出来的工厂方法模式下的三种方法了
此文仅博主个人学习心得,可能有些地方描述不够准确,欢迎指正