设计原则

单一职责原则【Single Responsibility Principle】
There should never be more than on reason for a class to change.
不应该有多于一个原因引起类的变更
public class SingleResponsibilityPrinciple {
    /**
     * Single Responsibility Principle【单一职责原则】:
     * There should never be more than on reason for a class to change.
     * 不应该有多于一个原因引起类的变更。
     */
    @Test
    public void all() {
        final UserImpl userImpl = new UserImpl();
        // 一个接口负责多项职责,接口层面违反单一职责原则
        userImpl.changeNameOrAge(true, "name", 0);
        userImpl.changeNameOrAge(false, null, 29);
        assertEquals("name", userImpl.getName());
        assertEquals(29, userImpl.getAge());

        final UserSRPImpl userSRPImpl = new UserSRPImpl();
        // 一个接口负责一项职责,符合单一职责原则
        userSRPImpl.changeName("name");
        userSRPImpl.changeAge(29);
        assertEquals("name", userSRPImpl.getName());
        assertEquals(29, userImpl.getAge());
    }

    interface User {
        // 单个接口方法负责多个职责,违反单一职责原则。
        void changeNameOrAge(boolean isName, String name, int age);
    }

    @Data
    static class UserImpl implements User {
        private String name;
        private int age;

        @Override
        public void changeNameOrAge(boolean isName, String name, int age) {
            if (isName) {
                setName(name);
            } else {
                setAge(age);
            }
        }
    }

    interface UserSRP {
        // 单个接口方法只实现一项功能,复合单一职责原则
        void changeName(String name);

        void changeAge(int age);
    }

    @Data
    static class UserSRPImpl implements UserSRP {
        private String name;
        private int age;

        @Override
        public void changeName(String name) {
            setName(name);
        }

        @Override
        public void changeAge(int age) {
            setAge(age);
        }
    }
}S

里氏替换原则【Liskov Substitution Principle】

1)if for each object o1 of type S there is an object o2 of type T such that
for all programs P defined in terms of T, the behavior of P is unchanged 
when o1 is substituted for o2 then S is a subtype of T.
对每个类型为 S 的对象 o1,都有一个类型为 T 的对象 o2,对于由 T 定义的应用程序 P,
当将 o2 替换为 o1 时,应用程序的行为没有变化,则 S 是 T 的子类型。

2)Functions that use pointers or references to base classes must be able to 
use objects of derived classes without knowing it。
所有使用指针或引用调用基类函数的地方都能透明地使用子类对象。

1)行为由接口或抽象类定义,由具体子类实现。
2)子类可以有独有的属性和方法。
3)重载父类方法时,放大形参的类型不会影响父类行为。
4)重写父类方法时,缩小方法的返回值类型不会影响父类行为。
public class LiskovSubstitutionPrinciple {
    /**
     * 里氏替换原则【Liskov Substitution Principle】
     * 1)if for each object o1 of type S there is an object o2 of type T such that
     * for all programs P defined in terms of T, the behavior of P is unchanged
     * when o1 is substituted for o2 then S is a subtype of T.
     * 对每个类型为 S 的对象 o1,都有一个类型为 T 的对象 o2,对于由 T 定义的应用程序 P,当将
     * o2 替换为 o1 时,应用程序的行为没有变化,则 S 是 T 的子类型。
     *
     * 2)Functions that use pointers or references to base classes must be able to
     * use objects of derived classes without knowing it。
     * 所有使用指针或引用调用基类函数的地方都能透明地使用子类对象【实际调用的是父类方法】。
     *
     * 1)行为由接口或抽象类定义,由具体子类实现。
     * 2)子类可以有独有的属性和方法。
     * 3)重载父类方法时,放大形参的类型不会影响父类行为。
     * 4)重写父类方法时,缩小方法的返回值类型不会影响父类行为。
     */
    @Test
    public void all() {
        final Father father = new Father();
        final Son son = new Son();

        final HashMap<String, Object> argsMap = Maps.newHashMap();
        final String fromFather = father.doThing(argsMap);
        final String fromSon = son.doThing(argsMap);
        assertEquals("父类方法 doThing 被调用", fromFather);
        assertEquals(fromFather, fromSon);

        /**
         * 调用方法时,优先选择类型匹配的方法。
         */
        final Map<String, Object> map = Maps.newHashMap();
        final String doThing = son.doThing(map);
        assertEquals("子类方法 doThing 被调用", doThing);

        final List<String> doCollect = son.doCollect();
        assertEquals(List.of("from","son"), doCollect);
    }

    static class Father{
        public String doThing(HashMap<String, Object> argsMap) {
            return "父类方法 doThing 被调用";
        }

        public Collection<String> doCollect() {
            return List.of("from","father");
        }
    }

    static class Son extends Father{
        /**
         * 子类重载方法的形参类型范围大于父类形参,传入父类形参时,子类方法不会被调用
         */
        public String doThing(Map<String, Object> argsMap) {
            return "子类方法 doThing 被调用";
        }

        /**
         * 子类重写方法的返回值类型可以小于父类
         */
        @Override
        public List<String> doCollect() {
            return List.of("from","son");
        }
    }
}

Dependence Inversion Principle【依赖倒置原则】

Hign level modules should not depend upon low level modules,Both should depend upon abstractions.
Abstractions should not depend upon details.Details should depend upon abstaractions.【面向接口编程】

1)高层模块不应该依赖低层模块,两者都应该依赖其抽象。
2)抽象不应该依赖于细节。
3)细节应该依赖于抽象。
     
基于 Java
1)模块间的依赖应该通过接口或抽象类发生,实现类之间不应该有直接的依赖关系。
2)接口或抽象类不应该依赖于实现类。
3)实现类依赖于接口或抽象类。
public class DependenceInversionPrinciple {
    /**
     * Dependence Inversion Principle【依赖倒置原则】:
     * Hign level modules should not depend upon low level modules,Both should depend upon abstractions.
     * Abstractions should not depend upon details.Details should depend upon abstaractions.【面向接口编程】
     * 1)高层模块不应该依赖低层模块,两者都应该依赖其抽象。
     * 2)抽象不应该依赖于细节。
     * 3)细节应该依赖于抽象。
     *
     * 基于 Java
     * 1)模块间的依赖应该通过接口或抽象类发生,实现类之间不应该有直接的依赖关系。
     * 2)接口或抽象类不应该依赖于实现类。
     * 3)实现类依赖于接口或抽象类。
     */
    @Test
    public void all() {
        final DriverImpl driver = DriverImpl.builder().name("driver").build();
        final BMW bmw = BMW.builder().name("bmw").build();
        final Benz benz = Benz.builder().name("benz").build();

        String drive = driver.drive(bmw);
        assertEquals("driver drive bmw", drive);
        // 司机换了一辆车开
        drive = driver.drive(benz);
        assertEquals("driver drive benz", drive);
    }

    interface Car{
        String run();
    }

    interface Driver{
        /**
         * 类间的依赖关系通过接口维护,实现了依赖倒置原则。类间耦合度低,可扩展性很高。
         */
        String drive(Car car);
    }

    @Data
    @Builder
    static class DriverImpl implements Driver{
        private String name;

        @Override
        public String drive(Car car) {
            return String.join(" ", name, "drive", car.run());
        }
    }

    @Data
    @Builder
    static class BMW implements Car {
        private String name;

        @Override
        public String run() {
            return name;
        }
    }

    @Data
    @Builder
    static class Benz implements Car {
        private String name;

        @Override
        public String run() {
            return name;
        }
    }

}

Interface Segregation Principe【接口隔离原则】

1)Clients should not be forced to depend upon interfaces that they don't use.
客户端不应该强制依赖于他们不需要使用的接口【对接口按特性进行细化,保证单一职责】。

2)The dependency of one class to another one should depend on the smallest possible interface.
类间的依赖关系应该建立在最小的接口上。

1)接口要尽量小。
2)接口的职责要保证单一。
3)接口要高内聚。
4)为外部访问定制服务接口。
public class InterfaceSegregationPrinciple {

    /**
     * Interface Segregation Principe【接口隔离原则】:
     * 1)Clients should not be forced to depend upon interfaces that they don't use.
     * 客户端不应该强制依赖于他们不需要使用的接口【对接口按特性进行细化,保证单一职责】。
     * 2)The dependency of one class to another one should depend on the smallest possible interface.
     * 类间的依赖关系应该建立在最小的接口上。
     *
     * 1)接口要尽量小。
     * 2)接口的职责要保证单一。
     * 3)接口要高内聚。
     * 4)为外部访问定制服务接口。
     */
    @Test
    public void all() {
        final BookService bookService = BookService.builder().build();
        bookService.addBook(Book.builder().name("设计模式之禅").author("秦小波").build());
        bookService.addBook(Book.builder().name("大话设计模式").author("陈杰").build());

        final IBook book = bookService;
        final Book byAuthor = book.byAuthor("秦小波");
        assertNotNull(byAuthor);
        assertEquals("秦小波", byAuthor.getAuthor());

        final Book byName = book.byName("Not existed");
        assertNull(byName);

        final IComplexBook complexBook = bookService;
        final Book build = Book.builder().name("设计模式之禅").author("陈杰").build();
        final Collection<Book> byBook = complexBook.byBook(build);
        assertEquals(2, byBook.size());
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class Book {
        private String name;
        private String author;
    }

    /**
     * 普通用户可执行的操作
     */
    interface IBook {
        Book byName(String name);

        Book byAuthor(String author);
    }

    /**
     * 管理员可执行的操作
     */
    interface IComplexBook extends IBook {
        Collection<Book> byBook(Book book);
    }

    @Builder
    static class BookService implements IComplexBook {
        @Default
        private final List<Book> books = new CopyOnWriteArrayList();

        public void addBook(Book book) {
            books.add(book);
        }

        @Override
        public Book byName(String name) {
            return findBook(name, Book::getName);
        }

        @Override
        public Book byAuthor(String author) {
            return findBook(author, Book::getAuthor);
        }

        private Book findBook(String name, Function<Book, String> mapper) {
            final Optional<Book> first = books.stream().filter(book -> StringUtils.equals(name, mapper.apply(book)))
                    .findFirst();
            return first.isPresent() ? first.get() : null;
        }

        @Override
        public Collection<Book> byBook(Book book) {
            if (book == null) {
                return Collections.emptyList();
            }

            return books.stream().filter(inner -> StringUtils.equals(book.getName(), inner.getName())
                    || StringUtils.equals(book.getAuthor(), inner.getAuthor())).collect(Collectors.toList());
        }
    }

}

Law Of Demeter【迪米特法则,又称最小知识原则(Least Knowledge Principle)】

Only talk to your immediate friends.
只和直接的朋友交流。

朋友列表
1)当前对象本身。
2)当前对象的实例属性直接引用的对象,包括集合和映射等对象容器。
3)当前对象的成员函数所引用的形参对象。
4)当前对象中创建的其他对象。
@Slf4j
public class LawOfDemeter {

    /**
     * Law Of Demeter【迪米特法则,又称最小知识原则(Least Knowledge Principle)】:
     * Only talk to your immediate friends.
     * 只和直接的朋友交流。
     *
     * 朋友列表
     * 1)当前对象本身。
     * 2)当前对象的实例属性直接引用的对象,包括集合和映射等对象容器。
     * 3)当前对象的成员函数所引用的形参对象。
     * 4)当前对象中创建的其他对象。
     */
    @Test
    public void all() {
        final IInstallImpl iInstallImpl = IInstallImpl.builder().build();
        final IInstallLODImpl iInstallLODImpl = IInstallLODImpl.builder().build();
        final InstallerImpl installerImpl = InstallerImpl.builder().build();

        installerImpl.install(iInstallImpl);
        installerImpl.install(iInstallLODImpl);
    }

    interface IInstall{
        boolean first();
        boolean second();
        boolean third();
    }

    interface IInstallLOD{
        boolean install();
    }

    interface Installer {
        boolean install(IInstall install);
        boolean install(IInstallLOD installLOD);
    }

    @Builder
    static class IInstallImpl implements IInstall{

        @Override
        public boolean first() {
            log.info("执行第一步");
            return nextBoolan();
        }

        private boolean nextBoolan() {
            return ThreadLocalRandom.current().nextBoolean();
        }

        @Override
        public boolean second() {
            log.info("执行第二步");
            return nextBoolan();
        }

        @Override
        public boolean third() {
            log.info("执行第三步");
            return nextBoolan();
        }
    }

    @Builder
    static class IInstallLODImpl implements IInstallLOD{

        @Override
        public boolean install() {
            return first() && second() && third();
        }

        public boolean first() {
            log.info("执行第一步");
            return nextBoolan();
        }

        private boolean nextBoolan() {
            return ThreadLocalRandom.current().nextBoolean();
        }

        private boolean second() {
            log.info("执行第二步");
            return nextBoolan();
        }

        private boolean third() {
            log.info("执行第二步");
            return nextBoolan();
        }
    }

    @Builder
    static class InstallerImpl implements Installer{

        @Override
        public boolean install(IInstall install) {
            /**
             * IInstall 接口暴露了太多的方法给 Installer,违反迪米特法则,同时也违反接口隔离原则。
             */
            if (install.first() && install.second() && install.third()) {
                log.info("安装成功");
                return true;
            }

            log.info("安装失败");
            return false;
        }

        @Override
        public boolean install(IInstallLOD installLOD) {
            // IInstallLOD 只暴露必须的方法给 Installer,符合迪米特法则
            final boolean install = installLOD.install();
            if (install) {
                log.info("安装成功");
            } else {
                log.info("安装失败");
            }

            return install;
        }
    }
}

Open Close Principle【开闭原则】

Software entities like classes, modules and functions should be open for extension but close for modification.
软件实体,像类,模块和方法应该对扩展开放,对修改关闭。
@Slf4j
public class OpenClosePrinciple {
    /**
     * Open Close Principle【开闭原则】:
     * Software entities like classes,modules and functions should be open for
     * extension but close for modification.
     * 软件实体,像类,模块和方法应该对扩展开放,对修改关闭。
     */
    @Test
    public void all() {
        final BookStore bookStore = BookStore.builder().build();
        final Book b1 = Book.builder().name("设计模式之禅").price(90).build();
        final Book b2 = Book.builder().name("大话设计模式").price(45).build();
        bookStore.addBook(b1);
        bookStore.addBook(b2);
        bookStore.sell();

        bookStore.addBook(new OffBook(b1.getName(), b1.getPrice(), 50, 0.9, 0.8));
        bookStore.addBook(new OffBook(b2.getName(), b2.getPrice(), 50, 0.9, 0.8));
        bookStore.sell();
    }

    @Builder
    @Data
    static class Book {
        private final String name;
        private final double price;
    }

    @Data
    @Builder
    static class BookStore {
        @Default
        private List<Book> books = new ArrayList();

        public void sell() {
            books.forEach(bk -> {
                log.info("开始销售图书 {} {}", bk.getName(), bk.getPrice());
            });
        }

        public void addBook(Book book) {
            books.add(book);
        }
    }

    static class OffBook extends Book {
        private int priceThreshold;
        private double highDiscount;
        private double lowDiscount;

        OffBook(String name, double price) {
            super(name, price);
        }

        public OffBook(String name, double price, int priceThreshold, double highDiscount, double lowDiscount) {
            super(name, price);
            this.priceThreshold = priceThreshold;
            this.highDiscount = highDiscount;
            this.lowDiscount = lowDiscount;
        }

        /**
         * 通过扩展原有的类并重写指定的方法实现新的特性,而不是去修改它
         */
        @Override
        public double getPrice() {
            final double price = super.getPrice();
            if (priceThreshold > 50) {
                return highDiscount * price;
            } else {
                return lowDiscount * price;
            }
        }
    }
}
原文地址:https://www.cnblogs.com/zhuxudong/p/10163413.html