instructions

Object-oriented Design Principles (GOF)

  • Program interfaces rather than implementations
  • Prefer object composition to inheritance

Creation pattern

This type of design pattern provides a way to hide the creation logic while creating an object, rather than instantiating the object directly using the new operator. This gives the program more flexibility in deciding which objects to create for a given instance.

The factory pattern

introduce

  • Purpose: To provide a unified class that can get object instances directly from the class name (single factory, or simple factory)
  • Key: Define an interface for creating objects and let subclasses decide which factory class to instantiate, deferring the creation of the class to subclasses
  • Scenario: 1. The type of the object can be determined only at runtime. 2. Switch databases and dynamically configure connections between different database types
  • Advantages: 1. Decouple the specific product from the creator; 2. Conform to the principle of single responsibility; 3. High scalability. If you want to add a product, just extend a factory class
  • Disadvantages: 1. Every time a product is added, a concrete class and object implementation factory need to be added, making the number of classes in the system multiply

Source application

Calendar.getInstance() java.text.NumberFormat.getInstance() java.util.ResourceBundle.getBundle()

java.net.URLStreamHandlerFactory javax.xml.bind.JAXBContext.createMarshaller

The Demo implementation

implementation

  1. Two hero factories are provided: AD and AP. The corresponding hero instance can be returned after passing in the hero name
  2. All heroes inherit the same interface;
  3. Use polymorphism to return instances

legend

steps

  1. Creating an interface

    public interface Legend {
    
        /** ** ** /
        String ult(a);
    }
    Copy the code
  2. Create the interface implementation class

    public class Garen implements Legend {
        private String garenParam;
    
        @Override
        public String ult(a) {
            return "Demacian Justice.";
        }
    
        public void setGarenMethod(String garenParam) {
            System.out.println("Galen's own method calls...");
            this.garenParam = garenParam; }}Copy the code
    public class XinZhao implements Legend{
        @Override
        public String ult(a) {
            return "Crescent Sweep."; }}Copy the code
    public class Teemo implements Legend{
        @Override
        public String ult(a) {
            return "Noshing Trap"; }}Copy the code
  3. Create an Ad,Ap factory class and generate the corresponding instance object according to the specified input parameters

    public class AdLegendFactory {
    
        /** * get the hero class */
        public Legend getLegend(String name){
    
            if ("garen".equalsIgnoreCase(name)) {
                Garen garen = new Garen();
                garen.setGarenMethod("Demacia");
                return garen;
            } else if ("xinzhao".equalsIgnoreCase(name)) {
                return new Teemo();
            }
            return null; }}Copy the code
    public class ApLegendFactory {
    
        public Legend getLegend(String name) {
            if ("teemo".equalsIgnoreCase(name)) {
                return new Teemo();
            }
            return null; }}Copy the code
  4. Use the factory class from the previous step, pass in the type information, get the object, and call the method

    public class FactoryPatternDemo {
    
        public static void main(String[] args) {
            AdLegendFactory adFactory = new AdLegendFactory();
            ApLegendFactory apFactory = new ApLegendFactory();
            String garenUlt = adFactory.getLegend("garen").ult();
            String teemoUlt = apFactory.getLegend("teemo").ult(); System.out.println(garenUlt); System.out.println(teemoUlt); }}Copy the code
  5. Execute the process

    Galen's own method calls... Demacian JusticeCopy the code

Abstract Factory pattern

introduce

  • Purpose: Factories that produce different product families
  • Key: Provide an interface to create a series of related or interdependent objects without specifying their specific classes. Also known as the factory of other factories
  • Scenario: 1. When the program needs to handle different families of related products, but you don’t want it to depend on specific classes of those products
  • Advantages: 1. You can be sure that the products you get from the factory are compatible with each other. 2. Conform to the principle of single responsibility; 3. Comply with the open and close principle
  • Disadvantages: 1. Poor scalability; 2. Add methods from other factory classes to the abstract factory class

Source application

java.sql.Connection java.sql.Driver

The Demo implementation

implementation

  1. Create a hero and equipment interface and implement that interface separately (different product families). Implementation classes are concrete heroes or equipment;
  2. A Factory class that defines two interfaces is called Factory. The function is to pass in the name of the hero or equipment, that is, to return the instance object of the specified hero or equipment.
  3. Create an abstract factory class that implements the two factory classes above
  4. Create a factory producer that provides an abstract factory class

legend

steps

  1. Create a hero interface

    public interface Legend {
    
        /** ** ** /
        String ult(a);
    }
    Copy the code
        /** * Equipment attributes **@return* /
        String stat(a);
    }
    Copy the code
  2. Create an abstract factory class

    public abstract class AbstractFactory {
        public abstract Legend getLegend(String legend);
        public abstract Item getItem(String item);
    }
    Copy the code
  3. Create the interface implementation class

    public class Garen implements Legend {
        private String garenParam;
    
        @Override
        public String ult(a) {
            return "Demacian Justice.";
        }
    
        public void setGarenMethod(String garenParam) {
            System.out.println("Galen's own method calls...");
            this.garenParam = garenParam; }}Copy the code
    public class XinZhao implements Legend {
        @Override
        public String ult(a) {
            return "Crescent Sweep."; }}Copy the code
    public class Teemo implements Legend {
        @Override
        public String ult(a) {
            return "Noshing Trap"; }}Copy the code
  4. Create an appliance interface

    public class SunFire implements Item {
        @Override
        public String stat(a) {
            return "Ar (armor)"; }}Copy the code
    public class TriForce implements Item {
        @Override
        public String stat(a) {
            return "AS,Crit,AD(Attack speed, critical strike, damage)"; }}Copy the code
    public class ZhonYa implements Item {
        @Override
        public String stat(a) {
            return "AP,AR"; }}Copy the code
  5. Create the interface implementation class

    public class LegendFactory extends AbstractFactory{
        @Override
        public Legend getLegend(String legend) {
            if ("garen".equalsIgnoreCase(legend)) {
                Garen garen = new Garen();
                garen.setGarenMethod("Demacia");
                return garen;
            } else if ("xinzhao".equalsIgnoreCase(legend)) {
                return new XinZhao();
            } else if ("teemo".equalsIgnoreCase(legend)) {
                return new Teemo();
            }
            return null;
        }
    
        @Override
        public Item getItem(String item) {
            return null; }}Copy the code
    public class ItemFactory extends AbstractFactory {
        @Override
        public Legend getLegend(String legend) {
            return null;
        }
    
        @Override
        public Item getItem(String item) {
            if (item == null) {
                return null;
            }
            if (item.equalsIgnoreCase("sunfire")) {
                return new SunFire();
            } else if (item.equalsIgnoreCase("triforce")) {
                return new TriForce();
            } else if (item.equalsIgnoreCase("zhonya")) {
                return new ZhonYa();
            }
            return null; }}Copy the code
  6. Create a hero factory class that inherits the abstract factory class and overrides the fetch instance method. Generates the corresponding instance object according to the specified input parameter

    public class LegendFactory extends AbstractFactory{
        @Override
        public Legend getLegend(String legend) {
            if ("garen".equalsIgnoreCase(legend)) {
                Garen garen = new Garen();
                garen.setGarenMethod("Demacia");
                return garen;
            } else if ("xinzhao".equalsIgnoreCase(legend)) {
                return new XinZhao();
            } else if ("teemo".equalsIgnoreCase(legend)) {
                return new Teemo();
            }
            return null;
        }
    
        @Override
        public Item getItem(String item) {
            return null; }}Copy the code
  7. Create an equipment factory class that inherits the abstract factory class and overrides the fetch instance method. Generates the corresponding instance object according to the specified input parameter

    public class ItemFactory extends AbstractFactory {
        @Override
        public Legend getLegend(String legend) {
            return null;
        }
    
        @Override
        public Item getItem(String item) {
            if (item.equalsIgnoreCase("sunfire")) {
                return new SunFire();
            } else if (item.equalsIgnoreCase("triforce")) {
                return new TriForce();
            } else if (item.equalsIgnoreCase("zhonya")) {
                return new ZhonYa();
            }
            return null; }}Copy the code
  8. Create a producer class or abstract factory instance

    public class FactoryProducer {
    
        public static AbstractFactory getFactory(String factory) {
            if ("legend".equalsIgnoreCase(factory)) {
                return new LegendFactory();
            } else if ("item".equalsIgnoreCase(factory)) {
                return new ItemFactory();
            }
            return null; }}Copy the code
  9. Execute the process

    public class AbstractFactoryPatternDemo {
    
        public static void main(String[] args) {
            AbstractFactory itemFactory = FactoryProducer.getFactory("item");
            AbstractFactory legendFactory = FactoryProducer.getFactory("LEGEND");
    
            Item triforce = itemFactory.getItem("triforce");
            System.out.println(triforce.stat());
    
            Legend timo = legendFactory.getLegend("teemo"); System.out.println(timo.ult()); }}Copy the code
  10. Terminal print

    AS,Crit,AD(Attack Speed,Crit, Attack)Copy the code

The singleton pattern

introduce

  • Purpose: To ensure that a class has only one instance object globally
  • Key: The constructor is private
  • Scenario: 1. Globally frequently used objects; 2. Heavyweight objects, no need for multiple objects, database connections, thread pools
  • Advantages: 1. Decouple the specific product from the creator; 2. Conform to the principle of single responsibility; 3. High scalability. If you want to add a product, just extend a factory class
  • Disadvantages: 1. No interface, cannot inherit; 2. How to instantiate a class that is only concerned with internal logic and not external

Source application

Spring & JDK

java.lang.Runtime org.springframework.aop.framework.ProxyFactoryBean org.springframework.beans.factory.support.DefaultSingletonBeanRegistry org.springframework.core.ReactiveAdapterRegistry

The Demo implementation

implementation

The following design patterns are designed with thread safety in mind

The hungry mode

  • Features: The initialization phase of the class load completes the initialization of the instance.

  • Design: Essentially using the JVM class loading mechanism to ensure instance uniqueness (initialization is performed only once) and thread-safety (the JVM performs the entire class loading process synchronously)

    public class HungrySingleton {
    
        /** * class load on static variable assignment initialization */
        private static final HungrySingleton instance = new HungrySingleton();
    
        /** * Constructor is private */
        private HungrySingleton(a) {}public static HungrySingleton getInstance(a) {
            returninstance; }}Copy the code

Lazy mode

  • Features: Lazy loading, instantiation starts only when it is actually used

  • Design: DCL two-end checklock optimization, volatile keyword to prevent compiler (JIT) instruction reordering from uninitialized instances

    public class LazySingleton {
    
        /** * Static properties, visibility, prevent instruction reordering */
        private volatile static LazySingleton instance;
    
        /** * Constructor is private */
        private LazySingleton(a) {}public static LazySingleton getInstance(a) {
            // DCL
            if (instance == null) {
                synchronized (LazySingleton.class) {
                    if (instance == null) {
                        return newLazySingleton(); }}}returninstance; }}Copy the code

Static inner class

  • Features: lazy loading that triggers class initialization only when it is actually used

  • Design: Essentially using the class loading mechanism to ensure thread-safety

    public class InnerClassSingleton implements Serializable {
        static final long serialVersionUID = 1L;
    
        private InnerClassSingleton(a) {
            // Prevent reflection damage
            if(InnerClassHolder.instance ! =null) {
                throw new RuntimeException("Singleton pattern does not allow multiple instances"); }}public static InnerClassSingleton getInstance(a) {
            return InnerClassHolder.instance;
        }
    
        /** * Simulate reflection attack **@param args
         */
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
            declaredConstructor.setAccessible(true);
            InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
            InnerClassSingleton instance = InnerClassSingleton.getInstance();
            System.out.println(innerClassSingleton == instance);
        }
    
        /** * Provides the readResolve() method to prevent deserialization * when the JVM deserializes a new object, the readResolve() method is automatically called to return the specified object, ensuring that the system does not generate multiple Java objects through deserialization **@return
         * @throws ObjectStreamException
         */
        Object readResolve(a) throws ObjectStreamException {
            return InnerClassHolder.instance;
        }
    
        /** * instance creation, only when the external class is called, starts the static inner class initialization, is lazy loading */
        private static class InnerClassHolder {
            private static final InnerClassSingleton instance = newInnerClassSingleton(); }}Copy the code

The enumeration

  • Features: There is no natural support for reflection to create corresponding instances, and has its own deserialization mechanism

  • Design: Essentially using the class loading mechanism to ensure thread-safety

    public enum EnumSingleton {
        /** * There is no natural support for reflection to create corresponding instances, and has its own deserialization mechanism * using class loading mechanism to ensure thread safety */
        ENUM_INSTANCE("singleton"."enum"),
        LAZY_INSTANCE("singleton"."lazy");
    
        private String name;
        private String type;
    
        EnumSingleton(String name, String type) {
            this.name = name;
            this.type = type; }}Copy the code

Reflection attack and serialization attack class reference example code

Builder model

introduce

  • Purpose: To separate the creation of a complex object from its representation, so that the same build process can create different representations
  • Key: Set the property and return the object itself
  • Scenario: 1. The object to be generated has a complex internal structure. 2. The internal attributes of the object to be generated depend on each other
  • Advantages: 1. Independent builder, easy to expand; 2. Easy to control detailed risks
  • Disadvantages: 1. As attributes change, so does the builder

The Demo implementation

Implementation Method 1

  • Create the hero class and internally implement the Builder mode

    @Data
    public class Champ {
    
        /** ** nickname */
        private String nickname;
    
        /**
         * 召唤师技能
         */
        private List<String> sums;
    
        /** * Passive skill */
        private String passive;
    
        /** * Role */
        private int roleId = 0;
    
        private Champ(String nickname, List<String> sums, String passive, int roleId) {
            this.nickname = nickname;
            this.sums = sums;
            this.passive = passive;
            this.roleId = roleId;
        }
    
        /** * private constructor, can only be created by the builder */
        private Champ(a) {}public static ChampBuilder builder(a) {
            return new ChampBuilder();
        }
    
        /** * builder */
        public static class ChampBuilder {
    
            private String nickname;
            private List<String> sums;
            private String passive;
            private int roleId = 0;
    
            public ChampBuilder nickname(String nickname) {
                this.nickname = nickname;
                return this;
            }
    
            public ChampBuilder setSums(String... sums) {
                this.sums = Arrays.asList(sums);
                return this;
            }
    
            public ChampBuilder setPassive(String passive) {
                this.passive = passive;
                return this;
            }
    
            public ChampBuilder setRoleId(int roleId) {
                this.roleId = roleId;
                return this;
            }
    
            /** * create */
            public Champ build(a) {
                // TODO check parameter...
                if (nickname == null || "".equalsIgnoreCase(nickname)) {
                    throw new RuntimeException("Name must not be empty.");
                }
                // Construct an instance
                Champ champ = new Champ();
                champ.setNickname(nickname);
                if(sums ! =null && sums.isEmpty()) {
                    champ.setSums(sums);
                }
                if(passive ! =null && "".equalsIgnoreCase(passive)) {
                    champ.setPassive(passive);
                }
                if (roleId > 0) {
                    champ.setRoleId(roleId);
                }
                returnchamp; }}}Copy the code
  • test

    public class BuilderPatternDemo {
        public static void main(String[] args) {
            Champ champ = Champ.builder().nickname("zhao xin").setPassive("fight").build(); System.out.println(champ); }}Copy the code
  • print

    Champ(nickname=zhao xin, sums=null, passive=null, roleId=0)
    Copy the code

Implementation 2

legend

  • Create summoner and equipment interface

    /** * Summoner */
    public interface Item {
        /** * name */
        String name(a);
    
        /** * attributes */
        double price(a);
    
        /** ** ** /
        Equipment equip(a);
    }
    Copy the code
    /** ** ** /
    public interface Equipment {
        /** ** ** /
        String equip(a);
    }
    Copy the code
  • Create the implementation class for the appliance

    /** * Attack equipment */
    public class Attack implements Equipment {
        @Override
        public String equip(a) {
            return "attack"; }}Copy the code
    /** * Defense equipment */
    public class Defense implements Equipment {
        @Override
        public String equip(a) {
            return "defense"; }}Copy the code
  • Creates an interface abstraction class for summoner item

    /** * Magic damage */
    public abstract class AbilityPower implements Item {
        @Override
        public abstract double price(a);
    
        @Override
        public Equipment equip(a) {
            return newAttack(); }}Copy the code
    /** ** ** */
    public abstract class MagicResistance implements Item {
        @Override
        public abstract double price(a);
    
        @Override
        public Equipment equip(a) {
            return newDefense(); }}Copy the code
  • Create a descendant class of the above abstract class

    /** * Missing section */
    public class LostChapter extends AbilityPower{
        @Override
        public String name(a) {
            return "Ghost book";
        }
    
        @Override
        public double price(a) {
            return 2700.0; }}Copy the code
    /** * Ludden's rebound */
    public class LudensEcho extends AbilityPower{
        @Override
        public String name(a) {
            return "The rise of Ludden.";
        }
    
        @Override
        public double price(a) {
            return 3100.0; }}Copy the code
    /** * Relic shield */
    public class RelicShield extends MagicResistance{
        @Override
        public String name(a) {
            return "Shield of Relics.";
        }
    
        @Override
        public double price(a) {
            return 2500.0; }}Copy the code
    /** * Uplifting armor (green armor) */
    public class SpiritVisage extends MagicResistance{
    
        @Override
        public String name(a) {
            return "Green armor";
        }
    
        @Override
        public double price(a) {
            return 2600.0; }}Copy the code
  • Create a composite class suit that contains the hero and equipment

    /**
     * 套装
     */
    public class Suit {
        private final List<Item> items = new ArrayList<>();
    
        public void addItem(Item item) {
            items.add(item);
        }
    
        public double getMoney(a) {
            return items.stream().map(Item::price).reduce(Double::sum).orElse(0.0);
        }
    
        public void showItems(a) { items.forEach(System.out::println); }}Copy the code
  • Create the Builder class, where subsequent extensions will be extended

    /** * Equipment builder */
    public class SuitBuilder {
    
        public Suit equipA(a) {
            Suit suit = new Suit();
            suit.addItem(new LostChapter());
            suit.addItem(new RelicShield());
            return suit;
        }
    
        public Suit equipB(a) {
            Suit suit = new Suit();
            suit.addItem(new LudensEcho());
            suit.addItem(new SpiritVisage());
            returnsuit; }}Copy the code
  • test

    /** * Builder pattern -- sample class */
    public class BuilderPatternDemo {
    
        public static void main(String[] args) {
            SuitBuilder suitBuilder = new SuitBuilder();
    
            Suit suit1 = suitBuilder.equipA();
            suit1.showItems();
            System.out.println(suit1.getMoney());
    
            System.out.println("= = = = = = = = = ="); Suit suit2 = suitBuilder.equipB(); suit2.showItems(); System.out.println(suit2.getMoney()); }}Copy the code

The prototype pattern

instructions

  • Purpose: Specify the type of object to create with prototype instances, and create new ones by copying these prototypes

    object

  • Key: implement Cloneable interface

  • Scenario: 1. The object to be generated has a complex internal structure. 2. The internal attributes of the object to be generated depend on each other

  • Advantages: 1. Improved performance; 2. Avoid constructor constraints

  • Disadvantages: 1. Cloneable interface must be implemented;

    In a real project, the prototype pattern rarely appears alone, but usually in conjunction with the factory method pattern, where a Clone method creates an object that is then provided to the caller by the factory method

The Demo implementation

Implementation Method 1

  • Implement cloneable interface

    @Data
    public class Champ implements Cloneable{
        
        private String name;
    
        @Override
        protected Object clone(a) {
            Object clone = null;
            try {
                clone = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return clone;
        }
    
        public static void main(String[] args) {
            Champ champ = newChamp(); Champ clone = (Champ) champ.clone(); }}Copy the code

Implementation 2

  • Integrated Factory model

  • Create an abstract class that implements the Cloneable interface

    @Data
    public abstract class Legend implements Cloneable{
        private String name;
    
        protected String type;
    
        /** ** ** /
        abstract String ult(a);
    
        @Override
        protected Object clone(a){
            Object clone = null;
            try {
                clone = super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            returnclone; }}Copy the code
  • Inheriting abstract classes

    public class Garen extends Legend{
        public Garen(a) {
            type = "garen123";
        }
    
        @Override
        String ult(a) {
            return "The Trial of Demacia."; }}Copy the code
    public class Teemo extends Legend{
        public Teemo(a) {
            type = "teemo123";
        }
    
        @Override
        public String ult(a) {
            return "Noshing Trap"; }}Copy the code
  • Caches classes to save instances

    public class LegendCache {
    
        private static ConcurrentHashMap<String, Legend> legendCache = new ConcurrentHashMap<>();
    
        public static Legend getLegend(String name) {
            Legend legend = legendCache.get(name);
            return (Legend) legend.clone();
        }
    
        public static void loadCache(a) {
            Legend garen = new Garen();
            garen.setName("garen");
            legendCache.put("garen",garen);
    
            Legend teemo = new Teemo();
            garen.setName("teemo");
            legendCache.put("teemo",teemo);
    
            Legend xinZhao = new XinZhao();
            garen.setName("xinzhao");
            legendCache.put("xinzhao",xinZhao); }}Copy the code
  • test

    public class PrototypePatternDemo {
    
        public static void main(String[] args) {
            LegendCache.loadCache();
    
            Legend teemo1 = LegendCache.getLegend("teemo");
            Legend teemo2 = LegendCache.getLegend("teemo");
            System.out.println(teemo1.hashCode());
            System.out.println(teemo2.hashCode());
    
            Teemo teemo = newTeemo(); Teemo teemo3 = (Teemo) teemo.clone(); Teemo teemo4 = (Teemo) teemo.clone(); System.out.println(teemo3.hashCode()); System.out.println(teemo4.hashCode()); }}Copy the code

    The article is from Cainiao College