几个常见的设计模式

1 工厂模式 工厂模式又细分为三种,分为简单工厂、工厂方法、抽象工厂这三种,分别介绍: 1)简单工厂模式:当需要产品类时,只需要传入一个对应该产品类的参数,

1. 工厂模式

工厂模式又细分为三种,分为简单工厂、工厂方法、抽象工厂这三种,分别介绍:

1)简单工厂模式:当需要产品类时,只需要传入一个对应该产品类的参数,就可以获取所需要的对象,无须知道其创建细节。

2) 工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类只能创建一个具体产品类的实例。 工厂方法模式让一个类的实例化延迟到其子类。在工厂方法模式中,不再提供一个统一的工厂类来创建所有的产品对象,而且针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

3)抽象工厂模式:抽象工厂有多个抽象产品类构成,每个抽象产品类可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。 抽象工厂模式和工厂方法模式最大的区别是,工厂方法针对的是产品等级,而抽象工厂创建的是子类产品工厂。

简单工厂模式

客户端在创建同一类别的不同实体对象时,客户只需传递不同参数到工厂类,工厂类会创建出不同的对象。这样减低了客户端与实体的耦合,但是每增加一种实体,都要在工厂类中增加相应的逻辑,违反了开放封闭原则。

fp

public class customer{ public staitic void main(String[] args){ ShapeFactory sf = new ShapeFactory(); sf.getShape("circle");//画圆 } } //图形工厂,生成图形用的 public class ShapeFactory { public Shape getShape(String type){ if(type.equals("circle")){ return new Circle(); }else if(type.equals("cquare")){ return new Square(); } return null; } } //多有形状的基类 public interface Shape { void draw(); } //形状的子类圆形 public class Circle implements Shape{ public void draw() { System.out.println("Draw Circle!"); } } //形状的子类正方形 public class Square implements Shape{ public void draw() { System.out.println("Draw Square!"
);    
    }
}
View Code

工厂方法模式

工厂方法模式有一个基类工厂,而且去掉了简单工厂中工厂方法的静态属性,用基类工厂的子类来产生不同的实体。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。 但每个实体都对应一个工厂类。其图如下:

 

1

//工厂方法客户端 public class Customer { public static void main(String[] args) { CircleFactory cf = new CircleFactory(); Circle c = cf.creatshape(); //圆工厂 SquareFactory sf = new SquareFactory(); Square s = sf.creatshape(); //正方形工厂 } } //工厂类的基类 public interface ShapeFactory { Shape creatshape(); } //正方形工厂,注意返回值 public class SquareFactory implements ShapeFactory { public Square creatshape() { return new Square(); } } // 圆形工厂,注意不要返回 Shape public class CircleFactory implements ShapeFactory{ public Circle creatshape() { return new Circle(); } } // 所有产品的基类 public interface Shape { void draw(); } //产品类 圆形 public class Circle implements Shape{ public void draw() { System.out.println("Draw Circle!"); } } //产品类 正方形 public class Circle implements Shape{ public void draw() { System.out.println("Draw Circle!"
);    
    }
}
View Code

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这里的超级工厂类为 AbstractFactory ,其子类有为生产一套产品各种不同的工厂 ,分别用来产生不同类型的实体。

public class Customer { public static void main(String[] args) { //生产红色的圆 AbstractFactory redcircle = new RedCircleFactory(); //生产绿色的正方形 AbstractFactory greensquare = new GreenSquareFactory(); } } //工厂的基类 public abstract class AbstractFactory{ abstract Shape getShape(); abstract Color getColor(); } // 绿色的正方形工厂 public class GreenSquareFactory extends AbstractFactory{ Shape getShape() { return new Square(); } Color getColor() { return new Green(); } } //红色的圆工厂 public class RedCircleFactory extends AbstractFactory{ Shape getShape() { return new Circle(); } Color getColor() { return new Red(); } } //以下为实体类 // 所有形状的基类 public interface Shape { void draw(); } // public class Circle implements Shape{ public void draw() { System.out.println("Draw Circle!"); } } //正方形 public class Square implements Shape{ public void draw() { System.out.println("Draw Square!"); } } //所有颜色的基类 public interface Color { void paint(); } //红色 public class Red implements Color{ public void paint() { System.out.println("Red!"); } } //绿色 public class Green implements Color { public void paint() { System.out.println("Green!"
);
    }
}
View Code

2.单例模式

单例写起来要区分开线程安全与非线程安全,而且根据何时初始化该类分为懒汉与饿汉模式,饿汉模式只要类加载就初始化完毕了,所以饿汉天生是线程安全的,非线程安全的就不写了,这里直接给出一下三种线程安全的方法:

饿汉式单例

声明为 static final 之后,单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}
View Code

双重检验锁单例

双重锁来自单重锁机制,即在产生实例的代码外部加上线程同步锁,因为在任何时候只能有一个线程调用 getInstance() 方法,且只是在第一次调用产生实例时才需要同步,单重锁效率低就是因为每次都执行同步操作。双重检验锁克服了这种缺点,加上后避免每次都同步,但加上第二重锁同样带来一个问题,主要在于instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。

  1. 给 instance 分配内存
  2. 调用 Singleton 的构造函数来初始化成员变量
  3. 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。所以最终的双重锁需要在实例对象前加上 volatile关键字 ,该是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。最终的代码如下:

public class Singleton {
    //声明成 volatile
    private volatile static Singleton instance; 
    private Singleton (){}
    
    public static Singleton getSingleton() {
        if (instance == null) {//第一重锁                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    } 
}
View Code

静态内部类单例(Effective java 推荐方法)

这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}
View Code

总结一下,一般情况直接饿汉,需要懒汉加载就静态类的模式即可,双重校验锁可以帮助懂点 java 基础。

3.策略模式

策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

 

public class Customet {
    public static void main(String[] args) {
        //构造  Context 时需要传递 Strategy 的子类对象
        Context c = new Context(new StrategyA());
        c.usestrategy();//用哪个子类就显示哪个子类策略
    }
}

public class Context {
    private Strategy strategy;
    // 构造一个具体策略方法
    public Context(Strategy strategy){
        this. strategy = strategy;
    }
    // 使用策略
    public void usestrategy(){
        strategy.use();
    } 
}

public interface Strategy {
    void use();
}

public class StrategyA implements Strategy{
    public void use() {
        System.out.println("use strategy A");
    }
}

public class StrategyB implements Strategy{
    public void use() {
        System.out.println("use strategy B");
    }
}
View Code

策略模式的特点是客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况,而且由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。