面向对象和设计模式(2)-- 设计模式之创建型

面向对象的基本设计原则(SOLID):

单一职责(Single Responsibility),类或者对象最好是只有单一职责,在程序设计中如果发现某个类承担着多种义务,可以考虑进行拆分。

开闭原则(Open-Close,Open for extension,close for modification),设计要对扩展开放,对修改关闭。换句话说,程序设计应保证平滑的扩展性,尽量避免因为新增同类功能而修改已有实现,这样可以少产出些回归问题。

里氏替换(Liskov Substitution),这是面向对象的基本要素之一,进行继承关系抽象时,凡是可以用父类或者基类的地方,都可以用子类替换。

接口分离(Interface Segregation),我们在进行类和接口设计时,如果再一个接口里定义了太多方法,其子类很可能面临两难,就是只有部分方法对它是有意义的,这就破坏了程序的内聚性。对于这种情况,可以通过拆分成功能单一的多个接口,将行为进行解耦。在未来维护中,如果某个接口设计有变,不会对使用其他接口的子类构成影响。

依赖反转(Dependency Inversion):

A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

也即针对接口编程,而不是针对实现编程。

合成复用原则(Composite Reuse Principle, CRP):复用时要尽量使用组合/聚合关系(关联关系),少用继承。

迪米特法则(Law of Demeter, LoD):一个软件实体应当尽可能少地与其他实体发生相互作用。

我觉得这个原则有两个意思:1、一个对象应该对其他对象保持最少的了解,对于被依赖的类而言,意思就是向外公开的public方法应该尽可能的少;2、不要和“陌生人”说话、只与你的直接朋友通信。直接朋友通常表现为关联,聚合和组成关系,两个对象之间联系很紧密,通常以成员变量,方法的参数和返回值的形式出现。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。


总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型:

1)单例模式( Singleton )

        保证一个类仅有一个实例,并提供一个访问它的全局控制点。让类自身负责保存它的唯一实例。 这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求 ), 并且它可以提供一个访问该实例的方法。

下图是利用 Java 的语言特性实现的线程安全且能延迟初始化的单例模式,Singleton 中维护着静态私有的 SingleHolder 类, SingleHolder 类中持有个静态常量 sHolder ,Client 若通过getSingleInstance 方法获取 Singleton 对象则直接返回 SingleHolder 类的 sHolder。

实现示例代码:

/**

 * “饿汉式”的单例实现方式

 *

 *可以保证线程安全

 */

public final class Director{

  /**

   *静态的本类实例

   */

  private static final Director INSTANCE = newDirector();


  /**

   *私有化的构造方法保证不被其它类调用

   */

  private Director() {

  }


  /**

   *客户端调用获取单例实例

   *

   *@return 单例实例

   */

  public static Director getInstance() {

    return INSTANCE;

  }

}

/**

 *采用枚举类型的单例模式

 */

public enum EnumDirector{

  @Override

  public String toString() {

    return getDeclaringClass().getCanonicalName() + "@" +hashCode();

  }

}

/**

 * “懒汉式”的单例模式

 *

 *内部类在被引用之前不会被类加载器加载,直到客户端调用的时候才被加载

 *

 *这个方案是线程安全的

 */

public final class LazyInitializationDirector{

  /**

   *私有构造方法

   */

  private LazyInitializationDirector() {

  }


  /**

   *@return 单例实例

   */

  public static  LazyInitializationDirector getInstance() {

    return InstanceHolder.INSTANCE;

  }


  /**

   *延迟加载,生成 {@link  LazyInitializationDirector} 的实例

   */

  private static class InstanceHolder{

    private static final  LazyInitializationDirector INSTANCE = newLazyInitializationDirector();

  }

}

/**

 *线程安全、双检查锁的单例模式

 *

 * volatile所修饰的变量可以被看作是一种 “程度较轻的 synchronized ”;

 *与  synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,

 *但是它所能实现的功能也仅是 synchronized 的一部分。

 *

 *锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。

 *互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协

 *调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,

 *它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的

 */

public final class ThreadSafeDoubleCheckLocking{

  private static volatile  ThreadSafeDoubleCheckLocking INSTANCE;


  private ThreadSafeDoubleCheckLocking() {

    // 防止通过反射进行实例化

    if  (null != INSTANCE) {

      throw new  IllegalStateException("该实例已经存在");

    }

  }


  public static  ThreadSafeDoubleCheckLocking getInstance() {

    // 采用局部变量的形式可以提高约 25% 的性能

    ThreadSafeDoubleCheckLocking instance= INSTANCE;

    // 如果已经被实例化则直接返回该实例

    if  (null ==instance) {

      // 无法确定其他的线程是否已经完成初始化

      // 为了确保我们需要锁定一个对象来进行确认

      synchronized (ThreadSafeDoubleCheckLocking.class) {

        // 再次将实例分配给局部变量,检查它是否被其他线程初始化

        // 在当前线程被阻塞进入锁定区域时。如果它被初始化则直接返回之前创建的实例

        instance= INSTANCE;

        if (null ==instance) {

          INSTANCE =  instance = newThreadSafeDoubleCheckLocking();

        }

      }

    }

    return instance;

  }

}

/**

 * “懒汉式”的单例模式-线程安全的

 */

public final class ThreadSafeLazyLoadDirector{

  private static volatile  ThreadSafeLazyLoadDirector INSTANCE;


  private ThreadSafeLazyLoadDirector() {

    // 防止通过反射进行实例化

    if  (null != INSTANCE) {

      throw new  IllegalStateException("该实例已经存在");

    }

  }


  /**

   *此方法被第一次调用时才会生成单例实例,实现懒加载

   */

  public static synchronized  ThreadSafeLazyLoadDirector getInstance() {

    if  (null == INSTANCE) {

      INSTANCE =  newThreadSafeLazyLoadDirector();

    }

    return INSTANCE;

  }

}

/**

 * Singleton

 */

public class Application{

  private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);


  public static void main(String[] args) {

    // “饿汉式” 单例模式

    Director director1= Director.getInstance();

    Director director2= Director.getInstance();

    LOGGER.info("饿汉式单例1 = {}", director1);

    LOGGER.info("饿汉式单例2 = {}", director2);


    // “懒汉式” 单例模式

    LazyInitializationDirector lazyDirector1=  LazyInitializationDirector.getInstance();

    LazyInitializationDirector lazyDirector2=  LazyInitializationDirector.getInstance();

    LOGGER.info("懒汉式单例1 = {}", lazyDirector1);

    LOGGER.info("懒汉式单例2 = {}", lazyDirector2);


    // 双检锁

    ThreadSafeDoubleCheckLocking  doubleCheckLocking1=  ThreadSafeDoubleCheckLocking.getInstance();

    LOGGER.info("双检锁单例1 = {}", doubleCheckLocking1);

    ThreadSafeDoubleCheckLocking  doubleCheckLocking2=  ThreadSafeDoubleCheckLocking.getInstance();

    LOGGER.info("双检锁单例2 = {}", doubleCheckLocking2);


    // 线程安全的“懒汉式”单例模式

    ThreadSafeLazyLoadDirector  lazyLoadDirector1=  ThreadSafeLazyLoadDirector.getInstance();

    LOGGER.info("线程安全的懒汉式单例1 = {}", lazyLoadDirector1.toString());

    ThreadSafeLazyLoadDirector  lazyLoadDirector2=  ThreadSafeLazyLoadDirector.getInstance();

    LOGGER.info("线程安全的懒汉式单例2 = {}", lazyLoadDirector2.toString());


    // 枚举型的单例模式

    EnumDirector enumDirector1= EnumDirector.INSTANCE;

    LOGGER.info("枚举型单例1 = {}", enumDirector1);

    EnumDirector enumDirector2= EnumDirector.INSTANCE;

    LOGGER.info("枚举型单例2 = {}", enumDirector2);

  }

}


2)工厂模式( Factory )

      针对每一种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品实例。

为每一种产品提供一个工厂类,不同工厂类实现同一个工厂接口,返回不同产品。

工厂模式

实现示例代码:

假定现在有两个厨师,一个只会做中餐,另一个只会做西餐,餐品分为熟食和生食两类。顾客需要顾客需要根据自己的口味来选择对应的厨师并告知其需要熟食还是生食,厨师根据顾客的口味来进行烹制。

/**

 *厨师

 */

public interface Cook{

  FoodcookFood(FoodType foodType);

}

/**

 *食物

 */

public interface Food{

  FoodTypegetFoodType();

}

/**

 *食物类型

 */

public enum FoodType{

  HOT("热的"), COLD("凉的");

  privateString name;

  FoodType(String foodType) {

    this.name =foodType;

  }


  public String getName() {

    returnname;

  }

}

/**

 *中餐厨师

 */

public class ChineseCook implements Cook{


  @Override

  public Food cookFood(FoodType foodType) {

    return newChineseFood(foodType);

  }

}

/**

 *中餐

 */

public class ChineseFood implements Food{

  privateFoodType foodType;

  public ChineseFood(FoodType foodType) {

    this.foodType =foodType;

  }


  @Override

  public FoodType getFoodType() {

    returnfoodType;

  }


  @Override

  public String toString() {

    return foodType.getName()

  + "中餐";

  }

}

/**

 *西餐厨师

 */

public class WesternCook implements Cook{

  @Override

  public Food cookFood(FoodType foodType) {

    return newWesternFood(foodType);

  }

}

/**

 *西餐

 */

public class WesternFood implements Food{

  privateFoodType foodType;

  public WesternFood(FoodType foodType) {

    this.foodType =foodType;

  }


  @Override

  public FoodType getFoodType() {

    returnfoodType;

  }


  @Override

  public String toString() {

    return foodType.getName()

  + "西餐";

  }

}

/**

 * Factory Method

 */

public class Application{

  private finalCook cook;

  private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

  private Application(Cook cook) {

    this.cook =cook;

  }


  public static void main(String[] args) {

    Application app= new Application(newWesternCook());

    app.makeFood();

    app= new Application(newChineseCook());

    app.makeFood();

  }


  private void makeFood() {

    Food food;

    food= cook.cookFood(FoodType.COLD);

    LOGGER.info(food.toString());

    food= cook.cookFood(FoodType.HOT);

    LOGGER.info(food.toString());

  }

}


3)抽象工厂模式( Abstract Factory )

      应对产品族概念而生。与工厂模式相比,抽象工厂模式是为了应对产品族。

实现示例代码:

假定一支远洋的队伍由船、船长以及水手组成,队伍有年轻和成熟之分。年轻的队伍有着年轻的船长、年轻的水手以及崭新的船只;而成熟的队伍有着老船长、老水手以及老旧的船只。不同队伍有着不同的主题,队伍中不同的物体存在一定的依赖性。

/**

 *船上成员以及船

 */

public interface Member{

  String getDescription();

}

/**

 *船长

 */

public interface Captain extends Member{

}

/**

 *水手

 */

public interface Sailor extends Member{

}

/**

 *船

 */

public interface Ship extends Member{

}

/**

 *团队工厂要实现的功能

 */

public interface TeamFactory{

  ShipcreateShip();

  CaptaincreateCaptain();

  SailorcreateSailor();

}

/**

 *老船长

 */

public class OldCaptain implements Captain{

  static final String DESCRIPTION = "我是一名老船长";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *老水手

 */

public class OldSailor implements Sailor{

  static final String DESCRIPTION = "我是一名老水手";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *旧船

 */

public class OldShip implements Ship{

  static final String DESCRIPTION = "我是一艘破旧的船";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *久经考验的团队

 */

public class PermanentTeamFactory implements TeamFactory{

  public Ship createShip() {

    return newOldShip();

  }


  public Captain createCaptain() {

    return newOldCaptain();

  }


  public Sailor createSailor() {

    return newOldSailor();

  }

}

/**

 *年轻船长

 */

public class YoungCaptain implements Captain{

  static final String DESCRIPTION = "我是一名年轻的船长";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *年轻水手

 */

public class YoungSailor implements Sailor{

  static final String DESCRIPTION = "我是年轻的水手";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *新船

 */

public class NewShip implements Ship{

  static final String DESCRIPTION = "我是一艘崭新的船";

  public String getDescription() {

    return DESCRIPTION;

  }

}

/**

 *年轻团队工厂

 */

public class YoungTeamFactory implements TeamFactory{

  public Ship createShip() {

    return newNewShip();

  }


  public Captain createCaptain() {

    return newYoungCaptain();

  }


  public Sailor createSailor() {

    return newYoungSailor();

  }

}

/**

 * AbstractFactory

 */

public class Application{

  private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

  privateShip mShip;

  privateCaptain mCaptain;

  privateSailor mSailor;


  public void createTeam(final TeamFactory factory) {

    setmCaptain(factory.createCaptain());

    setmShip(factory.createShip());

    setmSailor(factory.createSailor());

  }


  public static void main(String[] args) {

    Application app= newApplication();

    app.createTeam(newYoungTeamFactory());

    LOGGER.info("正在创建一支年轻的队伍...");

    LOGGER.info("-->" + app.getmCaptain().getDescription());

    LOGGER.info("-->" + app.getmShip().getDescription());

    LOGGER.info("-->" + app.getmSailor().getDescription());


    app.createTeam(newPermanentTeamFactory());

    LOGGER.info("正在创建一支久经考验的队伍...");

    LOGGER.info("-->" + app.getmCaptain().getDescription());

    LOGGER.info("-->" + app.getmShip().getDescription());

    LOGGER.info("-->" + app.getmSailor().getDescription());

  }


  public Ship getmShip() {

    returnmShip;

  }


  public void setmShip(Ship mShip) {

    this.mShip =mShip;

  }


  public Captain getmCaptain() {

    returnmCaptain;

  }


  public void setmCaptain(Captain mCaptain) {

    this.mCaptain =mCaptain;

  }


  public Sailor getmSailor() {

    returnmSailor;

  }


  public void setmSailor(Sailor mSailor) {

    this.mSailor =mSailor;

  }

}


4)建造者模式( Builder )

        将一个复杂对象的构建与它的表示分离,使得同样的构造过程可以产生不同的对象。

作为 Product 的内部类,Builder 统一了 Product 的整个构建过程,同时在 build 过程中,可以由于 set 值顺序不同等原因产生不同的效果。

实现示例代码:

假定需要生成一个虚拟的人物模型,人物主要包括几个简单的属性:姓名、年龄、国籍以及肤色。假如一个数据模型的拥有很多属性,如果单纯使用构造方法来实例化对象,势必会造成构造方法参数爆炸的问题,也可以称之为“反可伸缩构造方法模式”。代码的可读性和可靠性大大降低。此时,可以为这个对象创建的过程指定一个创建者,我们只需要向创建者描述该对象的一些具体细节,接下来的构造过程就统统交给创建者完成了。

/**

 *国籍

 */

public enum Nationality{

  CHINA("中国"), RUSSIA("俄罗斯"), USA("美国"), JAPAN("日本"), UK("英国");

  privateString name;


  Nationality(String name)  {

    this.name =name;

  }


  @Override

  public String toString() {

    returnname;

  }

}

/**

 *肤色

 */

public enum SkinColor{

  YELLOW("黄色"), BLACK("黑色"), WHITE("白色");

  private String color;


  SkinColor(String color) {

    this.color =color;

  }


  @Override

  public String toString() {

    returncolor;

  }

}

/**

 *人

 */

public final class Person{

  private finalString name;

  private finalInteger age;

  private finalNationality  nationality;


  public String getName() {

    returnname;

  }


  public Integer getAge() {

    returnage;

  }


  public Nationality getNationality() {

    returnnationality;

  }


  public SkinColor getSkinColor() {

    returnskinColor;

  }


  private finalSkinColor  skinColor;


  public Person(Builder builder) {

    this.name = builder.name;

    this.age = builder.age;

    this.skinColor = builder.skinColor;

    this.nationality = builder.nationality;

  }


  @Override

  public String toString() {

    StringBuilder sb= newStringBuilder();

    sb.append(name);

    if  (null !=nationality) {

      sb.append(" 是来自 " + nationality.toString() + " 的");

    }

    if  (null !=age) {

      sb.append(" " + age + "岁的");

    }

    if  (null !=skinColor) {

      sb.append(" 有着" + skinColor + "皮肤的");

    }

    sb.append("一个人");

    return sb.toString();

  }


  /**

   *创建者

   */

  public static class Builder{

    privateString name;

    privateInteger age;

    privateNationality nationality;

    privateSkinColor skinColor;


    public Builder age(Integer age) {

      this.age =age;

      return this;

    }


    public Builder name(String name) {

      if (null ==name) {

        throw new  IllegalArgumentException("人必须有名字!");

      }

      this.name =name;

      return this;

    }


    public Builder nationality(Nationality nationality) {

      this.nationality =nationality;

      return this;

    }


    public Builder skinColor(SkinColor skinColor) {

      this.skinColor =skinColor;

      return this;

    }


    public Person build() {

      return new  Person(this);

    }

  }

}

/**

 * Builder

 */

public class Application{

  private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

  public static void main(String[] args) {

    Person personWang=

            new Person.Builder().name("小王").age(25).nationality(Nationality.CHINA).skinColor(SkinColor.YELLOW).build();

    Person personZhang=

            new Person.Builder().name("小张").age(28).nationality(Nationality.USA).skinColor(SkinColor.WHITE).build();

    Person personLiu=

            new Person.Builder().name("老王").age(48).nationality(Nationality.JAPAN).skinColor(SkinColor.YELLOW).build();


    LOGGER.info(personWang.toString());

    LOGGER.info(personZhang.toString());

    LOGGER.info(personLiu.toString());

  }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容