门面模式
为了保证接口的可复用性(或者叫通用性),我们需要将接口尽量设计得细粒度一点,职责单一一点。但是,如果接口的粒度过小,在接口的使用者开发一个业务功能时,就会导致需要调用 n 多细粒度的接口才能完成。调用者肯定会抱怨接口不好用。粒度太大,会导致接口的通用性不好。
门面模式的应用场景举例
- 解决易用性问题
 门面模式可以用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层
 的接口。
- 解决性能问题
 过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 App 客户端的响应速度。
- 解决分布式事务问题
 借鉴门面模式的思想,再设计一个包裹这两个操作的新接口(新建用户和给用户新建钱包),让新接口在一个事务中执行两个 SQL 操作。
适配器是做接口转换,解决的是原接口和目标接口不匹配的问题。
 门面模式做接口整合,解决的是多接口调用带来的问题。
 适配器在代码结构上主要是继承加组合,门面模式在代码结构上主要是封装。
组合模式
主要是用来处理树形结构数据。
public abstract class FileSystemNode {
        protected String path;
        public FileSystemNode(String path) {
            this.path = path;
        }
        public abstract int countNumOfFiles();
        public abstract long countSizeOfFiles();
        public String getPath() {
            return path;
        }
    }
    public class File extends FileSystemNode {
        public File(String path) {
            super(path);
        }
        @Override
        public int countNumOfFiles() {
            return 1;
        }
        @Override
        public long countSizeOfFiles() {
            java.io.File file = new java.io.File(path);
            if (!file.exists()) return 0;
            return file.length();
        }
    }
    public class Directory extends FileSystemNode {
        private List<FileSystemNode> subNodes = new ArrayList<>();
        public Directory(String path) {
            super(path);
        }
        @Override
        public int countNumOfFiles() {
            int numOfFiles = 0;
            for (FileSystemNode fileOrDir : subNodes) {
                numOfFiles += fileOrDir.countNumOfFiles();
            }
            return numOfFiles;
        }
        @Override
        public long countSizeOfFiles() {
            long sizeofFiles = 0;
            for (FileSystemNode fileOrDir : subNodes) {
                sizeofFiles += fileOrDir.countSizeOfFiles();
            }
            return sizeofFiles;
        }
        public void addSubNode(FileSystemNode fileOrDir) {
            subNodes.add(fileOrDir);
        }
        public void removeSubNode(FileSystemNode fileOrDir) {
            int size = subNodes.size();
            int i = 0;
            for (; i < size; ++i) {
                if (subNodes.get(i).getPath().equalsIgnoreCase(fileOrDir.getPath())) {
                    break;
                }
            }
            if (i < size) {
                subNodes.remove(i);
            }
        }
    }
public class Demo {
        public static void main(String[] args) {
            /**
             * /
             * /wz/
             * /wz/a.txt
             * /wz/b.txt
             * /wz/movies/
             * /wz/movies/c.avi
             * /xzg/
             * /xzg/docs/
             * /xzg/docs/d.txt
             */
            Directory fileSystemTree = new Directory("/");
            Directory node_wz = new Directory("/wz/");
            Directory node_xzg = new Directory("/xzg/");
            fileSystemTree.addSubNode(node_wz);
            fileSystemTree.addSubNode(node_xzg);
            File node_wz_a = new File("/wz/a.txt");
            File node_wz_b = new File("/wz/b.txt");
            Directory node_wz_movies = new Directory("/wz/movies/");
            node_wz.addSubNode(node_wz_a);
            node_wz.addSubNode(node_wz_b);
            node_wz.addSubNode(node_wz_movies);
            File node_wz_movies_c = new File("/wz/movies/c.avi");
            node_wz_movies.addSubNode(node_wz_movies_c);
            Directory node_xzg_docs = new Directory("/xzg/docs/");
            node_xzg.addSubNode(node_xzg_docs);
            File node_xzg_docs_d = new File("/xzg/docs/d.txt");
            node_xzg_docs.addSubNode(node_xzg_docs_d);
            System.out.println("/ files num:" + fileSystemTree.countNumOfFiles());
            System.out.println("/wz/ files num:" + node_wz.countNumOfFiles());
        }
    }
享元模式
享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。
当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。
享元模式的代码实现非常简单,主要是通过工厂模式,在工厂类中,通过一个 Map 或者 List
 来缓存已经创建好的享元对象,以达到复用的目的。
棋盘中,棋子复用。这些相似对象的 id、text、color 都是相同的,唯独 positionX、positionY 不同。
 // 享元类
    public class ChessPieceUnit {
        private int id;
        private String text;
        private Color color;
        public ChessPieceUnit(int id, String text, Color color) {
            this.id = id;
            this.text = text;
            this.color = color;
        }
        public static enum Color {
            RED, BLACK
        }
// ...省略其他属性和getter方法...
    }
    public class ChessPieceUnitFactory {
        private static final Map<Integer, ChessPieceUnit> pieces = new HashMap<>();
        static {
            pieces.put(1, new ChessPieceUnit(1, "車", ChessPieceUnit.Color.BLACK));
            pieces.put(2, new ChessPieceUnit(2, "馬", ChessPieceUnit.Color.BLACK));
            //...省略摆放其他棋子的代码...
        }
        public static ChessPieceUnit getChessPiece(int chessPieceId) {
            return pieces.get(chessPieceId);
        }
    }
    //棋子
    public class ChessPiece {
        private ChessPieceUnit chessPieceUnit;
        private int positionX;
        private int positionY;
        public ChessPiece(ChessPieceUnit unit, int positionX, int positionY) {
            this.chessPieceUnit = unit;
            this.positionX = positionX;
            this.positionY = positionY;
        }
        // 省略getter、setter方法
    }
    public class ChessBoard {
        private Map<Integer, ChessPiece> chessPieces = new HashMap<>();
        public ChessBoard() {
            init();
        }
        private void init() {
            chessPieces.put(1, new ChessPiece(ChessPieceUnitFactory.getChessPiece(1), 0, 0));
            chessPieces.put(1, new ChessPiece(ChessPieceUnitFactory.getChessPiece(2), 1, 0));
            //...省略摆放其他棋子的代码...
        }
        public void move(int chessPieceId, int toPositionX, int toPositionY) {
            //...省略...
        }
    }
对于字体格式,我们可以将它设计成享元,让不同的文字共享使用。
    public class CharacterStyle {
        private Font font;
        private int size;
        private int colorRGB;
        public CharacterStyle(Font font, int size, int colorRGB) {
            this.font = font;
            this.size = size;
            this.colorRGB = colorRGB;
        }
        @Override
        public boolean equals(Object o) {
            CharacterStyle otherStyle = (CharacterStyle) o;
            return font.equals(otherStyle.font)
                    && size == otherStyle.size
                    && colorRGB == otherStyle.colorRGB;
        }
    }
    public class CharacterStyleFactory {
        private static final List<CharacterStyle> styles = new ArrayList<>();
        public static CharacterStyle getStyle(Font font, int size, int colorRGB) {
            CharacterStyle newStyle = new CharacterStyle(font, size, colorRGB);
            for (CharacterStyle style : styles) {
                if (style.equals(newStyle)) {
                    return style;
                }
            }
            styles.add(newStyle);
            return newStyle;
        }
    }
    public class Character {
        private char c;
        private CharacterStyle style;
        public Character(char c, CharacterStyle style) {
            this.c = c;
            this.style = style;
        }
    }
    public class Editor {
        private List<Character> chars = new ArrayList<>();
        public void appendCharacter(char c, Font font, int size, int colorRGB) {
            Character character = new Character(c, CharacterStyleFactory.getStyle(font, s
                    chars.add(character);
        }
    }
享元模式跟单例的区别:单例模式中,一个类只能创建一个对象。享元模式中,一个类可以创建多个对象,每个对象被多处代码引用共享。
 享元模式跟缓存的区别:享元模式的实现中,我们通过工厂类来“缓存”已经创建好的对象。缓存,主要是为了提高访问效率,而非复用。
 享元模式跟对象池的区别:池化技术中的“复用”可以理解为“重复使用”,主要目的是节省时间(比如从数据库池中取一个连接,不需要重新创建)。在任意时刻,每一个对象、连接、线程,并不会被多处使用。享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。
享元模式在 Java Integer 中的应用
private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
    }
除了 Integer 类型之外,其他包装器类型,比如 Long、Short、Byte 等,也都利用
 了享元模式来缓存 -128 到 127 之间的数据。
享元模式在 Java String 中的应用
		 String s1 = "小争哥";
		 String s2 = "小争哥";
		 String s3 = new String("小争哥");
		
		 System.out.println(s1 == s2);  //true
		 System.out.println(s1 == s3);  //false




















![[强网杯 2019]高明的黑客](https://img-blog.csdnimg.cn/9ccb67b989494987be07d745ed7bff20.png)