模式_模式识别 - CSDN
精华内容
参与话题
  • 常用10种设计模式详解

    万次阅读 多人点赞 2018-08-14 17:08:03
    一、单例模式 单例模式属于创建型模式,解决某个类频繁的创建与销毁。该模式保证了其对象在JVM中只有一个实例对象存在。必须保证私有化构造函数,只能有一个实例对象存在。 优点: 减少new关键字的使用,降低...

    一、单例模式

    单例模式属于创建型模式,解决某个类频繁的创建与销毁。该模式保证了其对象在JVM中只有一个实例对象存在。必须保证私有化构造函数,只能有一个实例对象存在。

    优点:

    • 减少new关键字的使用,降低系统内存的使用频率,同时减轻GC工作
    • 避免了资源的多重使用

    缺点:

    • 不可继承,没有接口。

    1、饿汉式

    • 优点:没有加锁同步,执行效率高

    • 缺点:当类加载时就初始化,没有懒加载,浪费内存。通过classloader 机制避免了多线程的同步问题

    HungrySingleton.java

    public class HungrySingleton {
    
        private static HungrySingleton hSingleton = new HungrySingleton();
    
        private HungrySingleton() {}
    
        public static HungrySingleton getInstance() {
            return hSingleton;
        }
    }

    2、懒汉式

    实现方式(1)
    • 优点:实现懒加载,实例化对象是在调用getInstance()

    • 缺点:没有加锁 synchronized,多线程下使用存在问题

    LazySingleton.java

    public class LazySingleton {
    
        private static LazySingleton instance = null;
    
        private LazySingleton(){}
    
        public static LazySingleton getInstance(){
            if(instance == null){
                instance = new LazySingleton();
            }
            return instance;    
        }
    }
    实现方式(2)
    • 改进:增加 synchronized 关键字,解决多线程问题

    • 不足:synchronized锁住了这个对象,每次调用getInstance()都会对对象上锁,这样大大降低了性能,事实上我们只有在第一次instance为空时才需要加锁

    SyncLazySingleton.java

    public class SyncLazySingleton {
    
        private static SyncLazySingleton instance = null;
    
        private SyncLazySingleton(){}
    
        public static synchronized SyncLazySingleton getInstance(){
            if (instance == null){
                instance = new SyncLazySingleton();
            }
            return instance;
        }
    }
    实现方式(3)(双重检测)
    • 该进:对instance做了判断,只有当instance为空时才对对象加锁,提升性能

    • 不足:依然存在小点问题(无序写入问题),例如:

      • 1、线程1、线程2进入getInstance()
      • 2、线程1首先进入synchronized线程同步,线程2等待线程1执行完成
      • 3、线程1判断instance为空则分配地址内存空间并实例化该类对象
      • 4、线程1执行完成退出
      • 5、线程2进入synchronized同步,此时instance已被线程1实例化,insatnce不为空,则返回线程1创建的instance实例。
    • 由于JVM无序写入问题,导致线程2有可能返回instance == null

    SyncLazySingleton2.java

    public class SyncLazySingleton2 {
    
        private static SyncLazySingleton2 instance = null;
    
        private SyncLazySingleton2 (){}
    
        public static SyncLazySingleton2 getInstance(){
            if (instance ==null){
                synchronized (SyncLazySingleton2.class){
                    if (instance == null){
                        instance = new SyncLazySingleton2();
                    }
                }
            }
            return instance;
        }
    }

    3、静态内部类

    • 优点:懒加载策略,线程安全。利用classloader加载机制实现初始化时只有一个线程,当InnerSingleton被加载时,instance不一定被初始化,应为没有调用SingleFactory没有被主动调用

    • 缺点:如果在构造函数中抛出异常,则将得不到实例

    InnerSingleton .java

    public class InnerSingleton {
    
        private static class SingleFactory{
            public static InnerSingleton instance = new InnerSingleton();
        }
    
        public static InnerSingleton getInstance(){
            return SingleFactory.instance;
        }
    
        public Object serialize(){
            return getInstance();
        }
    }

    3、枚举实现

    枚举详情参考

    • 过关键字enum创建枚举类型在编译后生成一个继承自java.lang.Enum类的类和另外一个类,其中Enum是抽象类,编译器还为我们生成了两个静态方法,分别是values()valueOf(),values()方法的作用就是获取枚举类中的所有变量,并作为数组返回,而valueOf(String name)方法与Enum类中的valueOf方法的作用类似根据名称获取枚举变量,上述两个方法需枚举实例向上转型为Enum

    • 使用枚举单例的写法,我们完全不用考虑序列化和反射的问题。枚举序列化是由JVM保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.EnumvalueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObjectreadObjectreadObjectNoDatawriteReplacereadResolve等方法,从而保证了枚举实例的唯一性。

    • 优点:线程安全,支持序列化机制,实现单例模式最佳做法(少数使用)

    • 缺点:未实现懒加载

    public enum EnumSingleton {
        INSTANCE;
        private String name;
        public String getName(){
            return name;
        }
        public void setName(String name){
            this.name = name;
        }
    }
    实现结果图

    这里写图片描述


    二、工厂模式

    特点:

    • 提供一种创建对象的最佳方式,在创建对象时不提供对外暴露创建逻辑,并且通过一个共同的接口来指向新创建的对象

    • 定义一个创建对象的接口,让子类来决定实例化哪一个具体的工厂类,延迟到子类去执行

    • 主要解决选择接口的问题

    • 扩展性高,只增加相应工厂类即可,知道名称即可创建对象,屏蔽具体的实现,调用者只关心接口

    • 增加需求时,需要增加具体类与工厂实现,导致类个数成倍增加,增加系统复杂度

    • 只有需要生成复杂类对象时才需要使用工厂模式,且简单工厂模式不属于23种设计模式

    (1)、简单工厂

    PersonAction.java

    public interface PersonAction {
        public void eat();
    }

    WomanFactory .java

    public class WomanFactory implements PersonAction {
        @Override
        public void eat() {
            Log.i("msg","woman");
        }
    }

    ManFactory.java

    public class ManFactory implements PersonAction {
        @Override
        public void eat() {
            Log.i("msg","man");
        }
    }

    PersonFactory 工厂类

    public class PersonFactory {
        public PersonAction eat(String type){
            if("woman".equals(type)){
                return new WomanFactory();
            }else if ("man".equals(type)){
                return new ManFactory();
            }else {
                Log.i("msg","输入类型不正确");
                return null;
            }
        }
    }

    使用如下

    public void onClick(View view){
            PersonFactory personFactory = new PersonFactory();
            PersonAction man = personFactory.eat("man");
            man.eat();
            PersonAction woman = personFactory.eat("woman");
            woman.eat();
        }
    (2)、多个方法

    修改PersonFactory.java如下

    public class PersonFactory {
    
        public PersonAction manEat(){
            return new ManFactory();
        }
    
         public PersonAction womanEat(){
            return new WomanFactory();
        }
    }
    (3)、静态方法

    修改PersonFactory.java如下

    public class PersonFactory {
    
        public static PersonAction manEat(){
            return new ManFactory();
        }
    
         public static  PersonAction womanEat(){
            return new WomanFactory();
        }
    }

    直接可通过类名调用方法

    1、工厂方法模式

    PersonAction.java

    public interface PersonAction {
        public void eat();
    }

    WomanEat.java

    public class WomanEat implements PersonAction {
        @Override
        public void eat() {
            Log.i("msg","woman is eating");
        }
    }

    ManEat.java

    public class ManEat implements PersonAction {
        @Override
        public void eat() {
            Log.i("msg","man is eating");
        }
    }

    Provider.java

    public interface Provider {
        public PersonAction produce();
    }

    WomanFactory .java

    public class WomanFactory implements Provider {
        @Override
        public PersonAction produce() {
            return new WomanEat();
        }
    }

    ManFactory.java

    public class ManFactory implements Provider {
        @Override
        public PersonAction produce() {
            return new ManEat();
        }
    }

    具体使用如下:

    ManFactory manFactory = new ManFactory();
    PersonAction action = manFactory.produce();
    action.eat();

    2、抽象工厂方法模式

    自己简化版

    LandAnimal.java

    public interface LandAnimal {
        public void run();
    }

    WaterAnimal .java

    public interface WaterAnimal {
        public void swim();
    }

    Cat .java

    public class Cat implements LandAnimal {
        @Override
        public void run() {
            Log.i("msg","cat is runing");
        }
    }

    Dog .java

    public class Dog implements LandAnimal {
        @Override
        public void run() {
            Log.i("msg","dog is runing");
        }
    }

    Fish .java

    public class Fish implements WaterAnimal {
        @Override
        public void swim() {
            Log.i("msg","Fish is swiming");
        }
    }

    Shark .java

    public class Shark implements WaterAnimal {
        @Override
        public void swim() {
            Log.i("msg","Shark is swiming");
        }
    }

    AbstractFactory .java

    public abstract class AbstractFactory {
        public abstract LandAnimal getDog();
        public abstract LandAnimal getCat();
        public abstract WaterAnimal getFish();
        public abstract WaterAnimal getShark();
    }

    AnimalFactory.java

    public class AnimalFactory extends AbstractFactory {
        @Override
        public LandAnimal getDog() {
            return new Dog();
        }
    
        @Override
        public LandAnimal getCat() {
            return new Cat();
        }
    
        @Override
        public WaterAnimal getFish() {
            return new Fish();
        }
    
        @Override
        public WaterAnimal getShark() {
            return new Shark();
        }
    }

    使用方法如下:

    AnimalFactory factory = new AnimalFactory();
    LandAnimal cat = factory.getCat();
    LandAnimal dog = factory.getDog();
    WaterAnimal fish = factory.getFish();
    WaterAnimal shark = factory.getShark();
    cat.run();
    dog.run();
    fish.swim();
    shark.swim();

    三、建造者模式(Builder)

    特点:

    • 在需要生成复杂内部结构时使用,即将多个简单的对象一步一步构建称为一个复杂对象。

    构建方式(1)

    新建Person.java类

    public class Person {
        public int age;
        public String name;
        public String sex;
        public String address;
        public String born;
    
        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    ", born='" + born + '\'' +
                    '}';
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getBorn() {
            return born;
        }
    
        public void setBorn(String born) {
            this.born = born;
        }
    }

    新建构造(builder)类PersonBuilder .java

    public class PersonBuilder {
    
        private Person mPerson;
    
        public PersonBuilder(){
            mPerson = new Person();
        }
        public PersonBuilder setAge(int age){
            mPerson.age = age;
            return this;
        }
    
        public PersonBuilder setName(String name){
            mPerson.name = name;
            return this;
        }
    
        public PersonBuilder setSex(String sex){
            mPerson.sex = sex;
            return this;
        }
    
        public PersonBuilder setAddress(String address){
            mPerson.address = address;
            return this;
        }
    
        public PersonBuilder setBorn(String born){
            mPerson.born = born;
            return this;
        }
    
        public Person create(){
            Log.i("msg", mPerson.toString());
            return mPerson;
        }
    }
    

    使用如下:

    public void onClick(View view){
            PersonBuilder builder = new PersonBuilder();
            builder.setAge(20)
                    .setName("张三")
                    .setAddress("北京市")
                    .setSex("男")
                    .setBorn("厦门")
                    .create();
            PersonBuilder builder1 = new PersonBuilder();
            builder1.setAge(22)
                    .setName("李四")
                    .setAddress("上海市")
                    .setSex("女")
                    .setBorn("福州")
                    .create();
        }

    看到这里是不是与构建对话框与OKHTTP3类似呢?

    这里写图片描述

    构建方式(2)

    新建Person.java类

    public class Person {
        public int age;
        public String name;
        public String sex;
        public String address;
        public String born;
    
        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    ", born='" + born + '\'' +
                    '}';
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getBorn() {
            return born;
        }
    
        public void setBorn(String born) {
            this.born = born;
        }
    }

    Ibuilder.java

    public interface Ibuilder {
        void setAge(int age);
        void setName(String name);
        void setAddress(String address);
        void setBorn(String born);
        void setSex(String sex);
    }

    ImplBuilder.java

    public class ImplBuilder implements Ibuilder {
    
        private Person mPerson;
    
        public ImplBuilder(){
            mPerson = new Person();
        }
    
        @Override
        public void setAge(int age) {
            mPerson.setAge(age);
        }
    
        @Override
        public void setName(String name) {
            mPerson.setName(name);
        }
    
        @Override
        public void setAddress(String address) {
            mPerson.setAddress(address);
        }
    
        @Override
        public void setBorn(String born) {
            mPerson.setBorn(born);
        }
    
        @Override
        public void setSex(String sex) {
            mPerson.setSex(sex);
        }
    
        public Person create(){
            Log.i("msg", mPerson.toString());
            return mPerson;
        }
    }
    

    使用如下:

    ImplBuilder builder = new ImplBuilder();
    builder.setAge(20);
    builder.setName("王五");
    builder.setAddress("南京");
    builder.setBorn("武汉");
    builder.setSex("男");
    builder.create();

    四、适配器模式

    特点:

    • 适配器继承或依赖已有的对象,实现想要的目标接口

    • 消除由于接口不匹配所造成的类的兼容性问题

    • 类的适配器模式、对象的适配器模式、接口的适配器模式

    • 提高了类的复用,增加了类的透明度

    (1)类的适配器模式

    通过实现目标接口,继承原有类,在原有类的基础上增加接口中方法,将一个类转换成满足另一个新接口的类

    Original.java

    public class Original {
        public void show(){
            Log.i("msg", "This is Original");
        }
    }

    ExpandInterfacen .java

    public interface ExpandInterfacen {
        void show();
        void hide();
    }

    NewAdapter .java

    public class NewAdapter extends Original implements ExpandInterfacen {
        @Override
        public void hide() {
            Log.i("msg", "This is NewAdapter");
        }
    }

    使用如下

    public void onClick(View view){
        NewAdapter adapter = new NewAdapter();
        adapter.hide();
        adapter.show();
    }

    这里写图片描述
    (2)对象的适配器模式

    不继承原有类,而是持有该类实例来实现兼容,将一个对象转换成满足另一个新接口的对象

    ObjectAdapter.java

    public class ObjectAdapter implements ExpandInterfacen {
    
        private Original mOriginal;
    
        public ObjectAdapter (){
            mOriginal = new Original();
        }
    
        @Override
        public void show() {
            mOriginal.show();
        }
    
        @Override
        public void hide() {
            Log.i("msg", "This is ObjectAdapter");
        }
    }

    使用如下

    ObjectAdapter adapter = new ObjectAdapter();
    adapter.show();
    adapter.hide();

    (3)接口的适配器模式

    不希望实现一个接口中所有的方法,可选择一个抽象类实现接口,然后继承该抽象类实现想实现的方法即可

    ExpandInterfacen.java

    public interface ExpandInterfacen {
        void show();
        void hide();
        void close();
        void open();
    }

    AbstractInterface.java

    public abstract class AbstractInterface implements ExpandInterfacen {
    
        @Override
        public void show() {
    
        }
    
        @Override
        public void hide() {
    
        }
    
        @Override
        public void close() {
    
        }
    
        @Override
        public void open() {
    
        }
    }

    Expand.java

    public class Expand extends AbstractInterface {
    
        @Override
        public void show() {
            super.show();
        }
    
        @Override
        public void close() {
            super.close();
        }
    }

    五、装饰器模式

    特点:装饰类持有原有类或接口的对象,并调用它的方法

    • 通过一个装饰类对现有类对象动态添加一些功能,同时不改变其结构

    • 动态添加,动态撤销

    • 继承的替代方式,继承只能静态添加

    • 多成装饰产生过多相似对象,复杂且不易排错

    这两参考了两种写法

    (1)一个装饰类

    Person,java

    public interface Person {
        void eat();
    }

    吃饭Petter.java

    public class Petter implements Person {
        @Override
        public void eat() {
            Log.i("msg", "petter is eating");
        }
    }

    装饰类PetterDecorator.java,饭前洗手,饭后睡觉

    public class PetterDecorator implements Person {
    
        Petter mPetter = null;
    
        public PetterDecorator(){
            mPetter = new Petter();
        }
    
        @Override
        public void eat() {
            wash();
            mPetter.eat();
            sleep();
        }
    
        public void sleep(){
            Log.i("msg", "eating after");
        }
    
        public void wash(){
            Log.i("msg", "eating before");
        }
    }

    使用如下:

    Person decorator = new PetterDecorator();
    decorator.eat();

    运行结果

    eating before
    petter is eating
    eating after

    (2)抽象装饰,多个装饰类

    Component抽象构件——Animal.java

    public interface Animal {
        void sleep();
    }

    ConcreteComponent 具体构件——Dog.java

    public class Dog implements Animal {
        @Override
        public void sleep() {
            Log.i("msg","dog is sleeping");
        }
    }

    ConcreteComponent 具体构件——Fish.java

    public class Fish implements Animal {
        @Override
        public void sleep() {
            Log.i("msg","fish is sleeping");
        }
    }

    Decorator装饰角色(一般为抽象类)——AbDecoratorAnimal.java

    public abstract class AbDecoratorAnimal implements Animal {
    
        private Animal mAnimal = null;
    
        public AbDecoratorAnimal(Animal animal){
            this.mAnimal = animal;
        }
    
        @Override
        public void sleep() {
            mAnimal.sleep();
        }
    }

    * 具体装饰角色——DogDecorator.java *

    public class DogDecorator extends AbDecoratorAnimal {
    
        public DogDecorator(Animal animal) {
            super(animal);
        }
    
        public void eatBone(){
            Log.i("msg","dog eats bone");
        }
    
        @Override
        public void sleep() {
            super.sleep();
            eatBone();
        }
    }

    具体装饰角色——DogDecorator2.java

    public class DogDecorator2 extends AbDecoratorAnimal {
    
        public DogDecorator2(Animal animal) {
            super(animal);
        }
    
        @Override
        public void sleep() {
            super.sleep();
            run();
        }
    
        private void run() {
            Log.i("msg","dog is running");
        }
    }

    * 具体装饰角色——FishDecorator.java *

    public class FishDecorator extends AbDecoratorAnimal {
    
        public FishDecorator(Animal animal) {
            super(animal);
        }
    
        public void swim(){
            Log.i("msg","fish is swimming");
        }
    
        @Override
        public void sleep() {
            super.sleep();
            swim();
        }
    }

    使用如下:

    Animal dog = new DogDecorator(new DogDecorator2(new Dog()));
    dog.sleep();
    Animal fish = new FishDecorator(new Fish());
    fish.sleep();

    运行结果:

    dog is sleeping
    dog is running
    dog eats bone
    fish is sleeping
    fish is swimming
    

    更多详细内容可参考java的 I / O

    我们来看一个实际案例吧!!!更好体验装饰器模式,参考设计模式|菜鸟教程

    Hero.java

    public interface Hero {
        void learnSkill();
    }

    BlindMonk.java

    public class BlindMonk implements Hero {
    
        public BlindMonk(String name){
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        private String name;
    
        @Override
        public void learnSkill() {
            Log.i("msg",getName());
        }
    }

    SkillDecorator.java

    public abstract class SkillDecorator implements Hero {
    
        private Hero mHero = null;
    
        public SkillDecorator(Hero hero){
            this.mHero = hero;
        }
    
        @Override
        public void learnSkill() {
            mHero.learnSkill();
        }
    }

    Qdecoraotr.java

    public class Qdecoraotr extends SkillDecorator {
    
        private String name;
    
        public Qdecoraotr(Hero hero, String name) {
            super(hero);
            this.name = name;
        }
    
        private void learn_Q(){
            Log.i("msg","习得技能"+name);
        }
    
        @Override
        public void learnSkill() {
            super.learnSkill();
            learn_Q();
        }
    }

    Wdecoraotr.java

    public class Wdecoraotr extends SkillDecorator {
    
        private String name;
    
        public Wdecoraotr(Hero hero, String name) {
            super(hero);
            this.name = name;
        }
    
        private void learn_W(){
            Log.i("msg","习得技能"+name);
        }
    
        @Override
        public void learnSkill() {
            super.learnSkill();
            learn_W();
        }
    }

    Edecoraotr.java

    public class Edecoraotr extends SkillDecorator {
    
        private String name;
    
        public Edecoraotr(Hero hero,String name) {
            super(hero);
            this.name = name;
        }
    
        private void learn_E(){
            Log.i("msg","习得技能"+name);
        }
    
        @Override
        public void learnSkill() {
            super.learnSkill();
            learn_E();
        }
    }

    Rdecoraotr.java

    public class Rdecoraotr extends SkillDecorator {
    
        private String name;
    
        public Rdecoraotr(Hero hero, String name) {
            super(hero);
            this.name = name;
        }
    
        private void learn_R(){
            Log.i("msg","习得技能"+name);
        }
    
        @Override
        public void learnSkill() {
            super.learnSkill();
            learn_R();
        }
    }

    使用如下:

    Qdecoraotr q = new Qdecoraotr(new BlindMonk("李青"),"Q");
    Wdecoraotr w = new Wdecoraotr(q, "W");
    Edecoraotr e = new Edecoraotr(w, "E");
    Rdecoraotr r = new Rdecoraotr(e, "R");
    r.learnSkill();

    运行结果

    李青
    习得技能Q
    习得技能W
    习得技能E
    习得技能R

    六、代理模式

    注意事项:
    1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
    2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

    解决问题:

    • 对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问,直接访问会给使用者或者系统结构带来很多麻烦

    • 租客 —— 中介 —— 房东

    ProxyInterface.java

    public interface ProxyInterface {
        void buy();
    }

    Tenant.java

    public class Tenant implements ProxyInterface{
    
        private String name;
    
        public Tenant(String name){
            this.name = name;
        }
    
        @Override
        public void buy() {
            Log.i("msg",name + " is buying");
        }
    }

    AgentProxy.java

    public class AgentProxy implements ProxyInterface{
    
        private Tenant mTenant;
        private String name;
    
        public AgentProxy(String name){
            this.name = name;
        }
    
        @Override
        public void buy() {
            if(mTenant == null){
                mTenant = new Tenant(name);
            }
            mTenant.buy();
        }
    }

    使用如下:

     new AgentProxy("张三").buy();

    七、原型模式

    注意事项:
    用于创建重复的对象,同时又能保证性能
    通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写 clone(),深拷贝是通过实现 Serializable 读取二进制流。
    浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
    深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

    浅复制

    public class ShadowClone implements Cloneable {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Object clone() throws CloneNotSupportedException{
            ShadowClone clone = (ShadowClone) super.clone();
            return clone;
        }
    }

    深复制

    public class DeepClone implements Cloneable,Serializable {
    
        public Object deepClone() throws IOException, ClassNotFoundException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
    
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return ois.readObject();
        }
    }

    八、备忘录模式

    特点:

    • 提供了一种可以恢复状态的机制

    • 实现了信息的封装,使得用户不需要关心状态的保存细节

    • 不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态

    原始数据类Original.java

    public class Original {
    
        private String value;
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        public Memo createMemo(){
            return new Memo(value);
        }
    
        public void restoreMemo(Memo memo){
            this.value = memo.getValue();
        }
    }

    备忘录类Memo.java

    public class Memo {
    
        private String value;
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        public Memo(String value) {
            this.value = value;
        }
    }

    保存备忘录类SaveMemo.java

    public class SaveMemo {
    
        private Memo mMemo;
    
        public SaveMemo(Memo memo) {
            this.mMemo = memo;
        }
    
        public Memo getMemo() {
            return mMemo;
        }
    
        public void setMemo(Memo memo) {
            this.mMemo = memo;
        }
    }

    使用方式如下:

        public void onClick(View view){
            Original original = new Original();
            original.setValue("hello");
            Log.i("msg",original.getValue());
            SaveMemo saveMemo = new SaveMemo(original.createMemo());
            Log.i("msg","修改后的值:");
            original.setValue("good");
            Log.i("msg",original.getValue());
            Log.i("msg","恢复初始值:");
            original.restoreMemo(saveMemo.getMemo());
            Log.i("msg",original.getValue());
        }

    运行结果

    hello
    修改后的值:
    good
    恢复初始值:
    hello

    九、观察者模式

    特点

    • 类和类之间的关系

    • 对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

    Observer.java

    public interface Observer {
        void update();
    }

    Observerable1.java

    public class Observerable1 implements Observer {
        @Override
        public void update() {
            Log.i("msg", "Observerable1 has received");
        }
    }

    Observerable2.java

    public class Observerable2 implements Observer {
        @Override
        public void update() {
            Log.i("msg", "Observerable2 has received");
        }
    }

    Subject.java

    public interface Subject {
        void add(Observer observer);
        void del(Observer observer);
        void notifyAllObservers();
        void opreation();
    }

    AbstractSubject.java

    这里写为抽象主要是为了展示扩展思路

    public abstract class AbstractSubject implements Subject{
    
        private Vector<Observer> mVector = new Vector<>();
    
        @Override
        public void add(Observer observer) {
            mVector.add(observer);
        }
    
        @Override
        public void del(Observer observer) {
            mVector.remove(observer);
        }
    
        @Override
        public void notifyAllObservers() {
            Enumeration<Observer> enumeration = mVector.elements();
            while (enumeration.hasMoreElements()){
                Observer observer = enumeration.nextElement();
                observer.update();
            }
        }
    
        @Override
        public void opreation() {
    
        }
    }

    RealizeSubject.java

    public class RealizeSubject extends AbstractSubject {
    
        @Override
        public void opreation() {
            Log.i("msg", "update");
            notifyAllObservers();
        }
    }

    使用方式如下:

      public void onClick(View view){
            Subject sub = new RealizeSubject();
            sub.add(new Observerable1());
            sub.add(new Observerable2());
            sub.opreation();
        }

    实现结果

    update
    Observerable1 has received
    Observerable2 has received

    十、策略模式

    特点

    • 多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护

    • 在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为

    • 缺点:策略类会增多,所有策略类都需要对外暴露

    Function.java

    public interface Function {
        int fun(int a,int b);
    }

    Addiction.java

    public class Addiction implements Function {
    
        @Override
        public int fun(int a, int b) {
            return a+b;
        }
    }

    Subtraction.java

    public class Subtraction implements Function {
        @Override
        public int fun(int a, int b) {
            return a-b;
        }
    }

    Division.java

    public class Division implements Function {
        @Override
        public int fun(int a, int b) {
            return a/b;
        }
    }

    Multiplication.java

    public class Multiplication implements Function {
        @Override
        public int fun(int a, int b) {
            return a*b;
        }
    }

    Execute.java

    public class Execute {
    
        private Function mFunction;
    
        public Execute(Function function){
            this.mFunction = function;
        }
    
        public int executeFunction(int a,int b){
            return mFunction.fun(a,b);
        }
    }

    使用如下:

      public void onClick(View view){
            Execute execute = new Execute(new Addiction());
            Log.i("msg",execute.executeFunction(5, 2)+"");
            execute = new Execute(new Division());
            Log.i("msg",execute.executeFunction(5, 2)+"");
            execute = new Execute(new Subtraction());
            Log.i("msg",execute.executeFunction(5, 2)+"");
            execute = new Execute(new Multiplication());
            Log.i("msg",execute.executeFunction(5, 2)+"");
        }

    本文只阐述常用的几种设计模式,如有其它需求,见下连接

    更多设计模式内容请移步:

    http://www.runoob.com/design-pattern/design-pattern-tutorial.html 设计模式|菜鸟教程

    https://www.cnblogs.com/geek6/p/3951677.html23种设计模式

    菜鸟努力行进……

    展开全文
  • 软件设计模式详解

    千次阅读 2020-04-01 08:36:10
    软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 ...

    软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性

    简介

    设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。这个术语的含义还存有争议。算法不是设计模式,因为算法致力于解决问题而非设计问题。设计模式通常描述了一组相互紧密作用的类与对象。设计模式提供一种讨论软件设计的公共语言,使得熟练设计者的设计经验可以被初学者和其他设计者掌握。设计模式还为软件重构提供了目标。

    随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且Ward Cunningham为此发明了WikiWiki用来交流设计模式的经验。

    历史

    肯特·贝克和沃德·坎宁安在1987年利用克里斯托佛·亚历山大在建筑设计领域里的思想开发了设计模式并把此思想应用在Smalltalk中的图形用户接口的生成中。一年后Erich Gamma在他的苏黎世大学博士毕业论文中开始尝试把这种思想改写为适用于软件开发。于此同时James Coplien 在1989年至1991 年也在利用相同的思想致力于C++的开发,而后于1991年发表了他的著作Advanced C++ Idioms。就在这一年Erich Gamma 得到了博士学位,然后去了美国,在那与Richard Helm, Ralph Johnson ,John Vlissides合作出版了Design Patterns - Elements of Reusable Object-Oriented Software 一书,在此书中共收录了23个设计模式。这四位作者在软件开发领域里也以他们的匿名著称Gang of Four(四人帮,简称GoF),并且是他们在此书中的协作导致了软件设计模式的突破。有时这个匿名GoF也会用于指代前面提到的那本书。

    模式格式

    尽管名称和顺序在不同的资料中各有不同,描述模式的格式大致分为以下四个主要部分:

    模式名称(Pattern Name):每一个模式都有自己的名字,模式的名字使得我们可以讨论我们的设计。

    问题(Problem):在面向对象的系统设计过程中反复出现的特定场合,它导致我们采用某个模式。

    解决方案(Solution):上述问题的解决方案,其内容给出了设计的各个组成部分,它们之间的关系、职责划分和协作方式

    效果(Consequence):采用该模式对软件系统其他部分的影响,比如对系统的扩充性、可移植性的影响。影响也包括负面的影响。

    别名(Also Known As):一个模式可以有超过一个以上的名称。这些名称应该要在这一节注明。

    动机(Motivation):该模式应该利用在哪种情况下是本节提供的方案(包括问题与来龙去脉)的责任。

    应用(Applicability)

    结构(Structure):这部分常用类图与互动图阐述此模式。

    参与者(Participants):这部分提供一份本模式用到的类与物件清单,与它们在设计下扮演的角色。

    合作(Collaboration):描述在此模式下,类与物件间的互动。

    结果(Consequences):这部分应描述使用本模式後的结果、副作用、与交换(trade-off)

    实现(Implementaion):这部分应描述实现该模式、该模式的部分方案、实现该模式的可能技术、或者建议实现模式的方法。

    例程(Sample Code):示范程式。

    已知应用(Known Uses):业界已知的实做范例。

    相关模式(Related Patterns):这部分包括其他相关模式,以及与其他类似模式的不同。

    模式列表

           设计模式分为三大类:创建型、结构型、行为型。创建型模式处理的是对象的创建过程(通过各种方式创建对象,使对象创建和管理变得简单),结构型模式处理的是对象/类的组合,行为型模式处理类和对象间的交互方式和任务分布(只有类可以创建对象,接口只能被实现)

    创建型

    抽象工厂模式(Abstract Factory)

           抽象工厂模式是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。区别于工厂方法模式的地方,工厂方法模式是创建一个工厂,可以实现多种对象;而抽象工厂模式是提供一个抽象工厂接口,里面定义多种工厂,每个工厂可以生产多种对象前者的重点在于"怎么生产",后者的重点在于"生产哪些";前者是一个抽象产品类,可以派生出多个具体产品类,后者是多个抽象产品类,每个抽象产品类可以派生出多个具体产品类;前者的每个具体工厂类只能创建一个具体产品类的实例,后者的每个具体工厂类可以创建多个具体产品类的实例。

    工厂方法模式(Factory Method)

           工厂方法模式的创建是因为简单工厂模式有一个问题,在简单工厂模式中类的创建依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以就出现了工厂方法模式,只需要创建一个工厂接口和多个工厂实现类,子类可以自己决定实例化哪一个工厂类,client类针对抽象接口进行编程,如果需要增加新的功能,继承工厂接口,直接增加新的工厂类就可以了,创建过程延迟到子类中进行,不需要修改之前的代码,满足了开闭原则,达到灵活地生产多种对象。。

    原型模式 (Prototype)

           原型模式是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其实就是将对象复制了一份并返还给调用者,对象需继承Cloneable并重写clone()方法。原型模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。分为浅复制和深复制,前者是将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的;后者是将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

    单例模式(Singleton)

           单例模式能保证一个类仅有一个实例,并提供一个访问它的全局访问点,同时在类内部创造单一对象,通过设置权限,使类外部无法再创造对象。单例对象能保证在一个JVM中,该对象只有一个实例存在,这样做的好处就在于如果某些类创建比较频繁,特别是对于一些大型的对象,这是一笔很大的系统开销。在创建的时候,省去了new操作符,降低了系统内存的使用频率,减轻了系统的压力。同时单例模式保证在一个jvm中仅存在一个实例的好处就在于好比一个军队当中只会存在一个最高级别的军官来指挥整个军队,这样才能保证独立控制整个过程,否则如果出现多个,肯定会杂乱无序。

    建造者模式(Builder)

           建造者模式是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,就好比是每个饭店或者每家都会做西红柿炒鸡蛋,最后虽然都是西红柿炒鸡蛋的外观,但是由于当中的做饭过程存在差别,所以味道会不同。在程序当中就是将一些不会变的基本组件,通过builder来进行组合,构建复杂对象,实现分离。这样做的好处就在于客户端不必知道产品内部组成的细节;同时具体的建造者类之间是相互独立的,对系统的扩展非常有利,满足开闭原则;由于具体的建造者类是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

    结构型

    适配器模式 (Adapter)

           将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

    桥接模式(Bridge)

           桥接模式是将抽象部分与实现部分分离,使它们都可以独立的变化。桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化(突然联想到了mvc模式)。将抽象化与实现化解耦,使得二者可以独立变化,就好比现在常说的mvc模式,view和model之间通过control来控制,达到高内聚低耦合来解耦的目的。

    组合模式(Composite)

           组合模式是将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。创建了一个包含自己对象组的类,并提供修改对象组的方法。在系统的文件和文件夹的问题上就使用了组合模式,文件下不可以有对象,而文件夹下可以有文件对象或者文件夹对象。

    修饰模式 (Decorator)

           装饰器模式是动态地给一个对象添加一些额外的职责,给一个对象增加一些新的功能,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。除了动态的增加,也可以动态的撤销,要做到动态的形式,不可以用继承实现,因为继承是静态的。

    外观模式

           外观模式是为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。在客户端和复杂系统之间再加一层,提供一个容易使用的外观层。外观模式是为了解决类与类之间的依赖关系的,外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,比如搜狐门户网站,就利用了外观模式。

    享元模式

           享元模式是运用共享技术有效地支持大量细粒度的对象。享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,重用现有的同类对象,若未找到匹配的对象,则创建新对象,这样可以减少对象的创建,降低系统内存,提高效率。

    代理模式(Proxy)

            代理模式是为其他对象提供一种代理以控制对这个对象的访问,也就是创建类的代理类,间接访问被代理类的过程中,对其功能加以控制。它和装饰器模式的区别在于,装饰器模式为了增强功能,而代理模式是为了加以控制。代理模式就是多一个代理类出来,替原对象进行一些操作,例如买火车票不一定在火车站买,也可以去代售点。再比如打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作。

    行为模式

    责任链模式 (Chain of Responsibility)

           责任链模式是避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求。在生活中学生进行请假的过程中,会涉及到,学生请假会一级一级往上批,最终处理,具体由谁批准可能不清楚。在程序当中,现在使用的struts拦截器即用到了责任链模式

    命令模式 (Command)

           命令模式是将一个请求封装成一个对象,从而使发出者可以用不同的请求对客户进行参数化。模式当中存在调用者、接收者、命令三个对象,实现请求和执行分开;调用者选择命令发布,命令指定接收者。举个例子,司令员下令让士兵去干件事情,司令员的作用是发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。司令士兵命令三者相互解耦,任何一方都不用去依赖其他人。其实struts框架也涉及到命令模式的思想。

    解释器模式(Interpreter)

           解释器模式是给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子,基本也就用在这个范围内,适用面较窄,例如:正则表达式的解释等。

    迭代器模式(Iterator)

           迭代器模式是提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。在Java当中,将聚合类中遍历各个元素的行为分离出来,封装成迭代器,让迭代器来处理遍历的任务;使简化聚合类,同时又不暴露聚合类的内部,在我们经常使用的JDK中各个类也都是这些基本的东西。

    中介者模式(Mediator)

           中介者模式是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。例如,MVC模式中control就是model和view的中介者。与适配器区别在于,适配器是为了兼容不同的接口,而中介者是为了将显示和操作分离

    备忘录模式 (Memento)

           备忘录模式是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。创建一个备忘录类,用来存储原始类的信息;同时创建备忘录仓库类,用来存储备忘录类,主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,也就是做个备份。在系统当中使用的撤销操作,即是使用了备忘录模式,系统可以保存有限次数的文件状态,用户可以进行上几个状态的恢复,也就是用到了备忘录模式。

    观察者模式(Observer)

           观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。也就是当被观察者状态变化时,通知所有观察者,这种依赖方式具有双向性,在QQ邮箱中的邮件订阅和RSS订阅,当我们浏览一些博客时,经常会看到RSS图标,意思就是,当你订阅了该文章,如果后续有更新,会及时通知你。这种现象即是典型的观察者模式。

    状态模式 (State)

           状态模式是允许对象在内部状态发生改变时改变它的行为。对象具有多种状态,且每种状态具有特定的行为。在网站的积分系统中,用户具有不同的积分,也就对应了不同的状态;还有QQ的用户状态有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态。

    策略模式 (Strategy)

           策略模式是定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,且算法的变化不会影响到使用算法的客户。是为了统一接口下的一系列算法类(也就是多种策略),用一个类将其封装起来,使这些策略可动态切换。策略模式属于行为型模式,是为了使这些策略可以相互切换,是为了选择不同的行为。

    模板方法模式(Template Method)

           模板方法模式是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。该模式就是在一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,将一些固定步骤、固定逻辑的方法封装成模板方法。调用模板方法即可完成那些特定的步骤。

    访问者模式 (Visitor)

           访问者模式主要是将数据结构与数据操作分离。在被访问的类里面加一个对外提供接待访问者的接口,访问者封装了对被访问者结构的一些杂乱操作,解耦结构与算法,同时具有优秀的扩展性。通俗来讲就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。

    模式原则

    大家都开始注意设计模式。那么,到底我们为什么要用设计模式呢?这么多设计模式为什么要怎么设计呢?说实话,以前我还真没搞清楚。就是看大家一口一个"Design pattern",心就有点发虚。于是就买了本"四人帮"的设计模式,结果看得似懂非懂:看得时候好像是懂了,过一会就忘了。可能是本人比较"愚钝"吧:))最近,有了点感悟。"独乐不如众乐",与大家分享一下,还望指教! 为什么要提倡"Design Pattern"呢?根本原因是为了代码复用,增加可维护性。那么怎么才能实现代码复用呢?OO界有前辈的几个原则:"开-闭"原则(Open Closed Principal)、里氏代换原则、合成复用原则。设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。

    开-闭原则

    此原则是由"Bertrand Meyer"提出的。原文是:"Software entities should be open for extension,but closed for modification"。就是说模块应对扩展开放,而对修改关闭。模块应尽量在不修改原(是"原",指原来的代码)代码的情况下进行扩展。那么怎么扩展呢?我们看工厂模式"factory pattern":假设中关村有一个卖盗版盘和毛片的小子,我们给他设计一"光盘销售管理软件"。我们应该先设计一"光盘"接口。而盗版盘和毛片是其子类。小子通过"DiscFactory"来管理这些光盘。代码为:

    public class DiscFactory{
        public static 光盘 getDisc(String name){
            return(光盘)Class.forName(name).getInstance();
        }
    }

    有人要买盗版盘,怎么实现呢?

    public class 小子{ 
        public static void main(String[] args){ 
            光盘 d=DiscFactory.getDisc("盗版盘"); 
            光盘.卖(); 
        } 
    }

    如果有一天,这小子良心发现了,开始卖正版软件。没关系,我们只要再创建一个"光盘"的子类"正版软件"就可以了。不需要修改原结构和代码。怎么样?对扩展开放,对修改关闭。"开-闭原则" 工厂模式是对具体产品进行扩展,有的项目可能需要更多的扩展性,要对这个"工厂"也进行扩展,那就成了"抽象工厂模式"。

    里氏代换原则

    里氏代换原则是由"Barbara Liskov"提出的。如果调用的是父类的话,那么换成子类也完全可以运行。比如: 光盘 d=new 盗版盘(); d.卖(); 要将"盗版盘"类改为"毛片"类,没问题,完全可以运行。Java编译程序会检查程序是否符合里氏代换原则。还记得java继承的一个原则吗?子类override方法的访问权限不能小于父类对应方法的访问权限。比如"光盘"中的方法"卖"访问权限是"public",那么"盗版盘"和"毛片"中的"卖"方法就不能是protected或private,编译不能通过。为什么要这样呢?你想啊:如果"盗版盘"的"卖"方法是private。那么下面这段代码就不能执行了: 光盘 d=new 盗版盘(); d.卖();可以说:里氏代换原则是继承复用的一个基础。

    合成复用原则

    就是说要少用继承,多用合成关系来实现。我曾经这样写过程序:有几个类要与数据库打交道,就写了一个数据库操作的类,然后别的跟数据库打交道的类都继承这个。结果后来,我修改了数据库操作类的一个方法,各个类都需要改动。"牵一发而动全身"!面向对象是要把波动限制在尽量小的范围

    在Java中,应尽量针对Interface编程,而非实现类。这样,更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系,"不要与陌生人说话"。这样,城门失火,才不至于殃及池鱼。扩展性和维护性才能提高

    理解了这些原则,再看设计模式,只是在具体问题上怎么实现这些原则而已。张无忌太极拳,忘记了所有招式,打倒了"玄幂二老",所谓"心中无招"。设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,可谓OO之最高境界。

    依赖倒转原则

    要针对接口编程,而不是针对实现编程。传递参数,或者在组合聚合关系中,尽量引用层次高的类。主要是在构造对象时可以动态的创建各种具体对象,当然如果一些具体类比较稳定,就不必在弄一个抽象类做它的父类,这样有画舌添足的感觉。

    接口隔离原则

    一种角色,不多不少,不干不该干的事,该干的事都要干

    抽象类

    抽象类抽象类不会有实例,类为子类继承,一般包含这个类的共同属性和方法。注意:好的继承关系中,只有叶节点是具体类,其他节点应该都是抽象类,也就是说具体类是不被继承的。将尽可能多的共同代码放到抽象类中。 

    展开全文
  • 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    万次阅读 多人点赞 2017-03-31 14:02:22
    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述  在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找...

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    1.概述

            在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。

    例子1:一个菜单功能能够根据用户的“皮肤”首选项来决定是否采用水平的还是垂直的排列形式。同事可以灵活增加菜单那的显示样式。

    例子2:出行旅游:我们可以有几个策略可以考虑:可以骑自行车,汽车,做火车,飞机。每个策略都可以得到相同的结果,但是它们使用了不同的资源。选择策略的依据是费用,时间,使用工具还有每种方式的方便程度 。



    2.问题

    如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?

    3.解决方案

    策略模式义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。(Definea family of algorithms,encapsulate each one, andmake them interchangeable. Strategy lets the algorithmvary independently from clients that use it. 

    策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。

    4.适用性

    当存在以下情况时使用Strategy模式
    1)• 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
    2)• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
    3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
    4)• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

    5.结构


    6.模式的组成

    环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
    抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
    具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。

    7.效果

    Strategy模式有下面的一些优点:
    1) 相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为。 继承有助于析取出这些算法中的公共功能。
    2) 提供了可以替换继承关系的办法: 继承提供了另一种支持多种算法或行为的方法。你可以直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到 Context中,而将算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且还不能动态地改变算法。最后你得到一堆相关的类 , 它们之间的唯一差别是它们所使用的算法或行为。 将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
    3) 消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有许多条件语句的代码通常意味着需要使用Strategy模式。
    4) 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间 /空间权衡取舍要求从不同策略中进行选择。

    Strategy模式缺点:

    1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类:  本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时 , 才需要使用Strategy模式。
    2 ) Strategy和Context之间的通信开销 :无论各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此很可能某些 ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的 ConcreteStrategy可能不使用其中的任何信息!这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题 , 那么将需要在Strategy和Context之间更进行紧密的耦合。
    3 )策略模式将造成产生很多策略类:可以通过使用享元模式在一定程度上减少对象的数量。 增加了对象的数目 Strategy增加了一个应用中的对象的数目。有时你可以将 Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其余的状态都由 Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的 Strategy不应在各次调用之间维护状态。

    8.实现

    1)出行旅游:

    uml:


    代码实现:

    <?php
    /**
    * 策略模式
    * 定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
    *
    */ 
    
    
    /**
    * 出行旅游
    *
    * 
    */
    interface TravelStrategy{
    	public function travelAlgorithm();
    } 
    
    
    /**
     * 具体策略类(ConcreteStrategy)1:乘坐飞机
     */
    class AirPlanelStrategy implements TravelStrategy {
    	public function travelAlgorithm(){
    		echo "travel by AirPlain", "<BR>\r\n"; 
    	}
    } 
    
    
    /**
     * 具体策略类(ConcreteStrategy)2:乘坐火车
     */
    class TrainStrategy implements TravelStrategy {
    	public function travelAlgorithm(){
    		echo "travel by Train", "<BR>\r\n"; 
    	}
    } 
    
    /**
     * 具体策略类(ConcreteStrategy)3:骑自行车
     */
    class BicycleStrategy implements TravelStrategy {
    	public function travelAlgorithm(){
    		echo "travel by Bicycle", "<BR>\r\n"; 
    	}
    } 
    
    
    
    /**
     * 
     * 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
     * 算法解决类,以提供客户选择使用何种解决方案:
     */
    class PersonContext{
    	private $_strategy = null;
    
    	public function __construct(TravelStrategy $travel){
    		$this->_strategy = $travel;
    	}
    	/**
    	* 旅行
    	*/
    	public function setTravelStrategy(TravelStrategy $travel){
    		$this->_strategy = $travel;
    	}
    	/**
    	* 旅行
    	*/
    	public function travel(){
    		return $this->_strategy ->travelAlgorithm();
    	}
    } 
    
    // 乘坐火车旅行
    $person = new PersonContext(new TrainStrategy());
    $person->travel();
    
    // 改骑自行车
    $person->setTravelStrategy(new BicycleStrategy());
    $person->travel();
    
    ?> 
    
    
    

     

    2)排序策略:某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作,

    如查找数组元素、对数组元素进行排序等。现以排序操作为例,使用策略模式设计该数组操作类,

    使得客户端可以动态地更换排序算法,可以根据需要选择冒泡排序或选择排序或插入排序,

    也能够灵活地增加新的排序算法。

    9.与其他相关模式

    1)状态模式

    策略模式和其它许多设计模式比较起来是非常类似的。策略模式和状态模式最大的区别就是策略模式只是的条件选择只执行一次,而状态模式是随着实例参数(对象实例的状态)的改变不停地更改执行模式。换句话说,策略模式只是在

    对象初始化的时候更改执行模式,而状态模式是根据对象实例的周期时间而动态地改变对象实例的执行模式。

    可以通过环境类状态的个数来决定是使用策略模式还是状态模式。
    策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,
    以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。
    使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换。
    如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式
    如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式

    2)简单工厂的区别:点击打开链接

    工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口. 让对象的创建与具体的使用客户无关。
    策略模式是对象行为型模式 ,它关注行为和算法的封装 。它定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。使得算法可独立于使用它的客户而变化

    用我们上面提到旅行的例子:
    我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,完全有客户自行决定去构建旅行方案(比如你自己需要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就可以了,然后由工厂代替你去构建具体方案(工厂代替你去买火车票)。

    上面的例子里面client代码:
    $person = new PersonContext(new TrainStrategy());
    $person->travel();
    我们看到客户需要自己去创建具体旅行(new TrainStrategy())实例。传递的是具体实例。
    而工厂模式你只要告诉哪种旅行就可以了,不是传递一个具体实例,而是一个标识(旅行方案标识)。

    10.总结与分析

    1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装它把算法的责任和算法本身分割开委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
    2)在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。2)
    3)策略模式仅仅封装算法,提供新算法插入到已有系统中以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

    转载指明原文出处:hguisu   设计模式 ( 十八 ) 策略模式Strategy(对象行为型) http://blog.csdn.net/hguisu/article/details/7558249






    感谢您的支持,我会继续努力的! 扫码打赏,你说多少就多少

             
    展开全文
  • 十种常用的设计模式

    千次阅读 2018-05-28 17:10:05
    转自:dean_hu设计模式总结1. 单例模式:实现方式:a) 将被实现的类的构造方法设计成private的。b) 添加此类引用的静态成员变量,并为其实例化。c) 在被实现的类中提供公共的CreateInstance函数,返回实例化的...

    转自:dean_hu

    设计模式总结

    1.       单例模式

    实现方式:

    a) 将被实现的类的构造方法设计成private的。

    b) 添加此类引用的静态成员变量,并为其实例化。

    c)  在被实现的类中提供公共的CreateInstance函数,返回实例化的此类,就是b中的静态成员变量。

     

    应用场景:

    优点: 
        1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 
        2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
        3.提供了对唯一实例的受控访问。 
        4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
        5.允许可变数目的实例。 
        6.避免对共享资源的多重占用。 
    缺点: 
        1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
        2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 
        3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 
        4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。 
    使用注意事项: 
        1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
        2.使用懒单例模式时注意线程安全问题 
        3.单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式) 
    适用场景: 
        单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 
        1.需要频繁实例化然后销毁的对象。 
        2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
        3.有状态的工具类对象。 
        4.频繁访问数据库或文件的对象。 
    以下都是单例模式的经典使用场景: 
        1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
        2.控制资源的情况下,方便资源之间的互相通信。如线程池等。 
    应用场景举例: 
        1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
        2. Windows的TaskManager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
        3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
        4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
        5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
        6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
        7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
        8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
        9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 
        10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例. 

     

    2.       策略模式

    实现方式:

    a)      提供公共接口或抽象类,定义需要使用的策略方法。(策略抽象类)

    b)      多个实现的策略抽象类的实现类。(策略实现类)

    c)       环境类,对多个实现类的封装,提供接口类型的成员量,可以在客户端中切换。

    d)      客户端 调用环境类 进行不同策略的切换。

    注:Jdk中的TreeSet TreeMap的排序功能就是使用了策略模式。


    策略模式的优点

      (1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。

      (2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

    策略模式的缺点

      (1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。

      (2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

     

    3.       代理模式
    一)静态代理

    实现方式:

    a) 为真实类和代理类提供的公共接口或抽象类。(租房)

    b) 真实类,具体实现逻辑,实现或继承a。(房主向外租房)

    c)  代理类,实现或继承a,有对b的引用,调用真实类的具体实现。(中介)

    d) 客户端,调用代理类实现对真实类的调用。(租客租房)

    二)动态代理

    实现方式:

    a) 公共的接口(必须是接口,因为Proxy类的newproxyinstance方法的第二参数必须是个接口类型的Class)

    b) 多个真实类,具体实现的业务逻辑。

    c)  代理类,实现InvocationHandler接口,提供Object成员变量,和Set方法,便于客户端切换。

    d) 客户端,获得代理类的实例,为object实例赋值,调用Proxy.newproxyinstance方法在程序运行时生成继承公共接口的实例,调用相应方法,此时方法的执行由代理类实现的Invoke方法接管。


    jdk动态代理使用的局限性
    通过反射类ProxyInvocationHandler回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

     

    4.       观察者模式

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

    实现方式:

    a) 角色抽象类(提供对观察者的添加,删除和通知功能)。

    b) 角色具体类,实现a,维护一个c的集合(对角色抽象类的实现)。

    c)  观察者抽象类(被角色通知后实现的方法)。

    d) 观察者实现类,实现c(多个)。

    注:JDK提供了对观察者模式的支持,使用Observable类和Observer接口


    两种模型(推模型和拉模型):

    ■  推模型是假定主题对象知道观察者需要的数据;而拉模型是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。

    ■  推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。

      

     

    5.       装饰模式:

    实现方式:

    a)       抽象的被装饰角色 (所有的角色都要直接或间接的实现本角色)

    b)       具体的被装饰角色,实现或继承a (被功能扩展的角色)

    c)       装饰角色,实现或继承a (本类有对a的引用,所有的具体装饰角色都需要继承这个角色)

    d)       多个具体修饰角色 ,继承c(对被装饰角色的功能扩展,可以任意搭配使用)


    意图: 

    动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。该模式以对客 户端透明的方式扩展对象的功能。

    适用环境:

    (1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

    (2)处理那些可以撤消的职责。

    (3)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的 子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

      

    6.      适配器模式:

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

    1.       类适配器(子类继承方式)

    实现方式:

    a)     目标抽象角色(定义客户要用的接口)

    b)     适配器(实现a继承c,作为一个转换器被客户调用)

    c)     待适配器(真正需要被调用的)

    d)     客户端(借用a的实例调用c的方法)


    2.     对象适配器(对象的组合方式)

    实现方式:

    a)     目标抽象角色(定义客户要用的接口)

    b)     适配器(实现a,维护一个c的引用,作为一个转换器被d调用)

    c)     待适配器(真正需要被调用的)

    d)     客户端(此类,借用a类的实例调用c类的方法,类似静态代理,但是解决的问题不同)


    3.     缺省的方式

    实现方式:

    a)     抽象接口

    b)     实现a的适配器类(空实现)

    c)     客户端,继承b,调用b中的方法,不必直接实现a(直接实现a需要实现a中的所有的方法)

    适配器模式的优点:

    1.     更好的复用性

      系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

    2.     更好的扩展性

    在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

    适配器模式的缺点:

      过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

     

     

    7.    命令模式

    将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或记录日志,以及支持可撤销的操作

    将“发出请求的对象”和”接收与执行这些请求的对象”分隔开来。

    实现方式:

    a)     抽象的命令角色 , 如:菜单(规定可以点哪些菜)

    b)     具体的命令角色(实现a 维护一个对c的引用),如:订单(已点的菜)

    c)     接收者(具体执行命令的角色),实际操作时,很常见使用"聪明"命令对象,也就是直接实现了请求,而不是将工作委托给c (弊端?) 如:厨师接收订单后做菜

    d)     调用者(维护一个对a的引用),如:服务员负责点菜并把订单推给厨师

    e)     客户端 调用d发出命令进而执行c的方法,如:顾客点餐

     

    效果:
    1)、command模式将调用操作的对象和实现该操作的对象解耦
    2)、可以将多个命令装配成一个复合命令,复合命令是Composite模式的一个实例
    3)、增加新的command很容易,无需改变已有的类
    适用性:
    1)、抽象出待执行的动作以参数化某对象
    2)、在不同的时刻指定、排列和执行请求。如请求队列
    3)、支持取消操作
    4)、支持修改日志
    5)、用构建在原语操作上的高层操作构造一个系统。支持事物


    8.    组合模式

    将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和复杂对象的使用具有一致性。

    实现方式:

    a)     抽象的构件接口 (规范执行的方法),b及c都需实现此接口,如:Junit中的Test接口

    b)     叶部件(实现a,最小的执行单位),如:Junit中我们所编写的测试用例

    c)     组合类(实现a并维护一个a的集合[多个b的组合]),如:Junit中的 TestSuite

    d)     客户端 可以随意的将b和c进行组合,进行调用

     

    什么情况下使用组合模式:

    当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了。

     

     

     

     

     

     

    9.    简单工厂模式

    就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。

    实现方式:

    a)     抽象产品类(也可以是接口)

    b)     多个具体的产品类

    c)     工厂类(包括创建a的实例的方法)

     

    优点:

    工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责消费对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。

       

    缺点:

    由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;

     

      

    10. 模板方法模式

    实现方式:

    a)     父类模板类(规定要执行的方法和顺序,只关心方法的定义及顺序,不关心方法实现)

    b)     子类实现类(实现a规定要执行的方法,只关心方法实现,不关心调用顺序)



     

     优点:

            1)封装不变部分,扩展可变部分:把认为不变部分的算法封装到父类实现,可变部分则可以通过继承来实现,很容易扩展。

            2)提取公共部分代码,便于维护。

           3)行为由父类控制,由子类实现。

     缺点:

            模板方法模式颠倒了我们平常的设计习惯:抽象类负责声明最抽象、最一般的事物属性和方法,实现类实现具体的事物属性和方法。在复杂的项目中可能会带来代码阅读的难度。


    dean_hu
    展开全文
  • 设计模式—build模式

    万次阅读 2017-12-29 07:56:55
    简介Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示...
  • 常见的几种设计模式

    千次阅读 2019-02-28 09:45:27
    应用spring中IOC解决的问题设计思想工厂模式简单工厂模式工厂方法模式观察者模式观察者模式的定义装饰模式模板方法定义:主要的作用:优点:缺点:应用场景:适配器模式(Adapter Pattern)定义类适配器对象适配器...
  • 设计模式(一):策略模式

    万次阅读 2019-03-01 21:37:32
    设计模式(一):策略模式 本文将以一个小Demo及其新需求来分析使用策略模式的好处。 设计模式简述: 设计模式: 1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用的经验。模式不是代码,而是某类...
  • ⼯⼚设计模式

    2020-05-28 16:56:46
    简单⼯⼚的设计 ... import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class BeanFactory { private static Properties env= new Properties();... InputStream inpu
  • 数据库Schema模式的理解——模式与数据库的区别

    万次阅读 多人点赞 2018-01-02 20:55:18
    模式与数据库、数据库中的表的关系:1个数据库下,可以有多个模式。 1个模式下,可以有0个或多个表 。   首先我来做一个比喻,什么是User,什么是Database,什么是Schema,什么是Table,什么是列,什么是行,? ...
  • 数据库模式

    千次阅读 2019-07-01 16:47:00
    模式(Schema) 定义:也称逻辑模式,是数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。 理解: ① 一个数据库只有一个模式; ② 是数据库数据在逻辑级上的视图; ③ 数据库模式以某一种数据...
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2019-07-27 08:07:21
    本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...
  • 史上最全设计模式导学目录(完整版)

    万次阅读 多人点赞 2020-08-29 13:26:34
    2012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式 + 简单工厂模式),为了方便大家学习,现将所有与设计模式学习相关文章...
  • 定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个...类型:创建类模式 类图: 工厂方法模式代码 [java]view plaincopy interfaceIProduct{ publicvoidproductMethod(); } c
  • 类型:创建类模式 类图: 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件: 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,...
  • java策略模式典型案例

    万次阅读 多人点赞 2020-10-08 10:58:02
    以一个顾客价格计算策略为背景,写一个策略模式的demo 参考代码 :https://github.com/zhang-xiaoxiang/DesignPatterns23 没有用策略模式我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现) //...
  • PHPWAMP站点管理的“域名模式”和“端口模式”详解、均支持无限自定义版本:phpwamp站点管理,站点管理使用,域名模式,端口模式,站点管理教程
  • 23种设计模式汇总整理

    万次阅读 多人点赞 2019-07-27 08:21:40
    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。 ...
  • Java实现单例模式的9种方法

    万次阅读 多人点赞 2019-12-22 14:19:05
    因进程需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。 二. 单例模式的特点 1、单例模式只能有一个实例。 2、单例类必须创建自己的唯一实例。 3、单例类...
  • 单例模式,参考完整代码在GitHub 地址:https://github.com/zhang-xiaoxiang/patter23 或者https://github.com/zhang-xiaoxiang/DesignPatterns23 一般三步走,1初始化2构造器私有3提供获取实例的方法 1单例模式---...
  • 单例模式的使用总结

    万次阅读 2020-04-21 11:20:12
    一、单例模式的定义和应用场景 (一)定义及基本要点 (二)应用场景 二、饿汉式单例模式 (一)基本代码展示分析 (二)基本分析和建议 三、懒汉式单例模式(双重检查锁) (一)基本代码展示分析 (二)...
1 2 3 4 5 ... 20
收藏数 4,782,453
精华内容 1,912,981
关键字:

模式