软件工程#02-固体
#初学者 #编程 #java #designpatterns

在软件的一生中,软件开发的大部分成本都用于维护软件。因此,作为开发人员,我们希望构建一些不皱眉而易于维护的东西。并且使用一些坚实的规则可以帮助我们实现这一目标

单一的责任原则

public class SonoplastaRodrigoFaro {

    // Audio é um texto em representação aos bordões
    private List<String> audios = new ArrayList<>();
    private Integer count = 0;

    public void addAudio(String audio) {
        StringBuilder stringBuilder = new StringBuilder();

        String stringNova = stringBuilder
                .append(++count)
                .append(":")
                .append(audio)
                .toString();

        audios.add(stringNova);
    }

    public void removeAudio(String audio) {
        audios.removeIf(a -> a.equals(audio));
    }

    public List<String> getAudios() {
        return audios;
    }

    public Optional<String> getAudio(String audio) {
        return audios.stream()
                .filter(a -> a.equals(audio))
                .findFirst();
    }
}
}

我们在这里有Sonoplast类,它具有其属性,是输入计数器和音频列表,所有这些都对这些属性进行了。

ã班级只有合理的责任,没有持久性,也没有其他回应。

持久守则:

public class SonoplastaPersistence {

    public final static String PATH = "/home/pedro/Documentos/audios.txt";

    public void persist(List<String> audios) throws IOException {

        try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(PATH))) {

           String entradas = audios
                   .stream()
                   .map(item -> String.valueOf(item))
                   .collect(Collectors.joining("|"));

           bufferedWriter.write(entradas);

            System.out.println("Persistido com sucesso!");

        } catch (IOException e) {
            throw new IOException("Error on persist the archive");
        }
    }
}

持久性类别以其期刊的注释为其偏爱的方向生成文件。

public class SRP {
    public static void main(String[] args) throws IOException {
        SonoplastaRodrigoFaro sonoplasta = new SonoplastaRodrigoFaro();

        sonoplasta.addAudio("CAVALOO");
        sonoplasta.addAudio("UUUIII");
        sonoplasta.addAudio("ELE GOXXTA");
        sonoplasta.addAudio("Que isso meu fio calma");

        sonoplasta.getAudios()
                .forEach(System.out::println);

        sonoplasta.getAudio("1:CAVALOO")
                .ifPresentOrElse(audio -> System.out.println("Audio: " + audio), () -> System.out.println("Audio não existe"));


        SonoplastaPersistence sonoplastaPersistence = new SonoplastaPersistence();

        sonoplastaPersistence.persist(sonoplasta.getAudios());
    }
}

最后,另一个呼叫这两个类的班级。

操作的演示:

Image description

arquivovoment persidit:

Image description

开放关闭原则

这是一个经过深入讨论的原则。他说,必须关闭课程才能开放以进行扩展。这意味着一类完成后,不应修改它而是继承,这会产生一些不适,因为我们无法更改我们的课程?遵循此原则的方法是使用抽象,抽象类甚至接口。我将展示两种使用此原则的方法。

可能您已经在生活中创建了一个计算器,在此计算器中应该是:

if (action == "+") {
  doSum();
} else if (action == "-") {
  doSubtraction();
}

,从你开始。这伤害了OCP,为什么?如果我们用这两个妈妈创建计算类,那么我们将在以后再实施两个,这将是分区和乘法,我们将更改现成的类别。我们如何解决这个问题?

首先,让我们创建一个称为iCalulation的界面:

public interface ICalculation {
     void perform();
}

它具有一个名为“绩效”的座右铭,该表演将用于所有基本操作。

我们现在将为每个基本操作创建一个类,实现接口。


public class Multiplication implements ICalculation{

    private double left;
    private double right;
    private double result;

    @Override
    public void perform() {
        result = left * right;
        System.out.println("Result of multiplication: " + result);
    }

    public Multiplication(double left, double right) {
        this.left = left;
        this.right = right;
        this.result = 0.0;
    }

    public double getLeft() {
        return left;
    }

    public void setLeft(double left) {
        this.left = left;
    }

    public double getRight() {
        return right;
    }

    public void setRight(double right) {
        this.right = right;
    }


}

对于每个基本操作,只需更改pertric()即可。我们意识到,这样做,我们的代码是丰富的,并且根据每个操作的实现来运行。示例:

public class OCP {
    public static void main(String[] args) {
        ICalculation calculation = new Multiplication(2, 2);
        calculation.perform();
    }
}

我们可以看到,我们不需要为每个操作做出几个决策结构,只需通过我们想要的实现实例即可。上面的一个示例正在通过两个号之间的乘法进行传递。如果我们想要一个和总和,我们将通过总和等等。

记住,将此原理带到字母中。

规格模式与OCP非常有效

让我们通过将实用的OCP放置一个称为规范的模式来使用另一个示例。

注意:我安装了腰部,因为由于示例的大小,我们将在同一类中进行所有操作,在第30页中不需要三十行。构造函数。>

public class FilterBySpecification {
    public static void main(String[] args) {
        var iphone = new Product(6.7, "iphone", 1800);
        var motorola = new Product(6.8, "motorola", 900);
        var iphoneX = new Product(6.9, "iphone", 3800);

        var listProducts = List.of(iphone, motorola, iphoneX);

        FilterProduct filterProduct = new FilterProduct();

        filterProduct.filterByName(listProducts, "iphone")
                .forEach(System.out::println);

    }

    static class FilterProduct {
        public List<Product> filterByName(List<Product> products, String name) {
            return products.stream()
                    .filter(product -> product.getName().equals(name))
                    .collect(Collectors.toList());
        }
    }

    @Data
    @AllArgsConstructor
    @ToString
    static class Product {
        public double size;
        public String name;
        public double price;

    }
}

在上方,我们有3个简单的类,一个产品,一个用于母亲©全部用于产品过滤。我们可以看到,如果您的老板也要求您按价格过滤,那么我们已经按名称过滤了产品,这是对的,Poring?然后大小?然后按价格和规模等等?您会在准备好的班级中添加更多妈妈吗?滤波器围栏?好吧,它将通过公开封闭的原则来工作,因此我们可以使用称为规范的模式实施:

public class FilterBySpecification {
    interface Specification<T> {
        boolean isSatisfied(T item);
    }

    public static void main(String[] args) {
        var iphone = new Product(6.7, "iphone", 1800);
        var motorola = new Product(6.8, "motorola", 900);
        var iphoneX = new Product(6.9, "iphone", 3800);

        var listProducts = List.of(iphone, motorola, iphoneX);

        FilterItens filterProduct = new FilterItens();

        filterProduct.filterItem(listProducts, new NameSpecification("iphone"))
                .forEach(System.out::println);

    }

    interface Filter<T> {
        Stream<T> filterItem(List<T> itens, Specification<T> specification);
    }

    static class FilterItens implements Filter<Product> {

        @Override
        public Stream<Product> filterItem(List<Product> itens, Specification<Product> specification) {
            return itens.stream()
                    .filter(specification::isSatisfied);
        }
    }

    @AllArgsConstructor
    static class NameSpecification implements Specification<Product> {
        private String name;

        @Override
        public boolean isSatisfied(Product item) {
            return item.getName().equals(name);
        }
    }


    @Data
    @AllArgsConstructor
    @ToString
    static class Product {
        public double size;
        public String name;
        public double price;

    }
}

我们在这里做的事情:我们创建了一个具有规范名称的天才界面,该界面的名称为“ Issatistied()”,它返回布尔值,我们将以我们的规格,大小,价格和名称来实现此母亲。这个母亲都在为每个规范过滤我们的物品。我们还创建了一个名为“ filterimes”的类,该类别接收产品列表和一个规范,可以是尺寸,价格,名称,我们只能使用一个母亲©all,尊重开放式封闭原则。如果出现了新的过滤器需求,只需创建一个新的规范,例如:sizespecification和extrice sizespecification to namessepification Investments。这不会在我们的班级中移动,该班级具有业务规则,过滤,尊重原则。操作的示例:

Image description

bi或更多规格:例如,每个过滤器也可以制作一个或多个规格,例如,我想同时按名称和名称过滤产品。示例:

在我们的方式中,将添加另一个接口和一个类:

    interface BiFilter<T> {
        Stream<T> filterItens(List<T> itens, Specification<T> specification1, Specification<T> specification2);
    }

    static class BiFilterItens implements BiFilter<Product> {
        @Override
        public Stream<Product> filterItens(List<Product> itens, Specification<Product> specification1, Specification<Product> specification2) {
            return itens.stream()
                    .filter(item -> specification1.isSatisfied(item) && specification2.isSatisfied(item));
        }
    }

他将在同一滤镜中找到两个规格。在母亲©所有主体中实现此目的:

    public static void main(String[] args) {
        var iphone = new Product(6.7, "iphone", 1800);
        var motorola = new Product(6.8, "motorola", 900);
        var iphoneX = new Product(6.9, "iphone", 3800);

        var listProducts = List.of(iphone, motorola, iphoneX);

        BiFilterItens filterProduct = new BiFilterItens();

        filterProduct.filterItens(listProducts, new NameSpecification("iphone"), new SizeSpecification(6.9))
                .forEach(System.out::println);

    }

请注意,我们创建了一个称为大小的新规范,您将在其中完成头晕的大小

    @AllArgsConstructor
    static class SizeSpecification implements Specification<Product> {
        private double size;

        @Override
        public boolean isSatisfied(Product item) {
            return item.getSize() == size;
        }
    }

,我们将此sizespecient作为第二个参数传递给了我们的过滤器。

成本结果:

Image description

Liskov替代原则

Liskov替换的原理决定了派生类必须由其基类代替。

在实践中,所有女儿(通过脚跟实施)必须保持与父级相同的行为(阶级是继承)。

让我们假设我们有一个人班,带有CPF,RG,名字和一个与人与母亲的人相同属性的学生班,并意识到我们可以让学生继承一个人?

但是,我们必须注意不要违反这一原则,尤其是在以下情况下:

  • 哭泣/实施一个不做任何事情的母亲。
  • 返回父亲班的不同类型和价值。

例如,如果在父类中,CPF返回字符串,则在子类中,CPF无法返回很长时间。想象母班是女儿班的多态性合同。

实际上,这是一个非常有原因的例子,让我们尝试思考日常生活中更常见的东西,例如社交媒体。

让我们去以下课程:


public abstract class MediaSocial {
    void chamadaEmGrupo() {
        // Faça algo
    }

    void publicarPost() {
        // faça algo
    }

    void criarGrupos () {
        // faça algo
    }

    void chatComAmigo() {
        // faça algo
    }
}

我们看到我们有一个名为社交媒体的课程,该课程具有社交网络的所有功能,例如正确的Facebook?

public class Facebook extends MediaSocial{

    void chamadaEmGrupo() {
        // Faça algo
    }

    void publicarPost() {
        // faça algo
    }

    void criarGrupos () {
        // faça algo
    }

    void chatComAmigo() {
        // faça algo
    }
}

我们可以看到Facebook实施了中世代阶级的所有动议,因此没有违反原则,因为一个人可以替换另一个而不会破坏风险。

现在,如果WhatsApp继承了社交媒体?

public class Whatsapp extends MediaSocial{

        void chamadaEmGrupo() {
            // Faça algo
        }

        void publicarPost() {
            // faça algo
        }

        void criarGrupos () {
            // faça algo
        }

        void chatComAmigo() {
            // faça algo
        }
}

我们可以看到WhatsApp没有帖子,然后...繁荣!因此,女儿班级WhatsApp不能取代父亲班。 Enti不遵循LSP

的原理

但是我们如何解决这个问题?我将在Solid的Litrinha I(接口)的演示中向您展示:

**注意:请记住,如果有可能避免使用Herarit,使用和滥用界面以及其他实心王子,LSP可以成为一种反疗法,这意味着,有时,有时是附加的复杂性不值得补偿。除了某些情况**

接口隔离

您意识到,在上面的示例中,中世代摘要类正在实施一百个母亲,使所有WhatsApp,Instagram和其他社交网络无法实施,因为母亲的数量©所有资源中的所有母亲©

我们将隔离界面,使停止更美丽和可用。

首先,让我们拿出这个中介类,创建一个称为MediatoCial
的接口

interface MediaSocial {
    void chatComAmigo();
    void chamadaDeVoz();
}

这个接口,只有一个责任,是私人聊天

另一个称为MediaGroup的接口:

interface MediaGrupos {
    void chamadaEmGrupo();
    void criarGrupos ();
}

具有小组职责。

和另一个称为MediaFeed的接口:

interface MediaFeed {
    void publicarPost();
    void verPosts();
}

负责人的新闻提要。

现在,让我们调整Facebook和WhatsApp课程:

public class Facebook implements MediaSocial, MediaGrupos, MediaFeed{

    @Override
    public void publicarPost() {

    }

    @Override
    public void verPosts() {

    }

    @Override
    public void chamadaEmGrupo() {

    }

    @Override
    public void criarGrupos() {

    }

    @Override
    public void chatComAmigo() {

    }

    @Override
    public void chamadaDeVoz() {

    }
}

由于Facebook具有我们隔离的所有接口函数,它将全部实现,whatsapp:

public class Whatsapp implements MediaGrupos, MediaSocial{

        @Override
        public void chamadaEmGrupo() {

        }

        @Override
        public void criarGrupos() {

        }

        @Override
        public void chatComAmigo() {

        }

        @Override
        public void chamadaDeVoz() {

        }
}

只是实现两个接口,因为WhatsApp没有新闻feed。

意识到我们实施了这一原则的收益和抽象?一生的最佳原则之一!

依赖性反转

依赖性反转基本上说,它不是根据具体类别而定,而是根据抽象的,高的潜水员不应取决于楼下,两者都应取决于抽象§§o。

好吧,让我们创建一个模拟付款的类

public class Payment {
    private CartaoCredito cartaoCredito;
    private BoletoBancario boletoBancario;
    private DebitoCartao debitoCartao;

    public Payment(CartaoCredito cartaoCredito, BoletoBancario boletoBancario, DebitoCartao debitoCartao) {
        this.cartaoCredito = cartaoCredito;
        this.boletoBancario = boletoBancario;
        this.debitoCartao = debitoCartao;
    }

    public void doPayment() {
        // FAz o pagamento 
    }
}

我们正在使用的方式,我们正在使用依赖的注射,这很酷,但是除此之外,根据DIP原则,我们的班级太错了3其他具体课程,Plasocredito,Bultobancario和Debitocartation,还记得吗?我们可能不依赖于实施,而是取决于抽象...

因此,让我们创建一个称为PaymayInterface的接口

public interface PaymentInterface {
    void doSomething(double valor);
}

现在让我们进行3种支付课程实现此接口。


public class DebitoCartao implements PaymentInterface{
    @Override
    public void doSomething(double valor) {
        System.out.println("Pagamento feito no débito, valor: " + valor);
    }
}

,我们将为所有其他人做此实施。

现在在我们的付款课上,让我们对其进行重新启动以满足蘸酱。


public class Payment {
    private PaymentInterface meioPagamento;

    public Payment(PaymentInterface meioPagamento) {
        this.meioPagamento = meioPagamento;
    }

    public void doPayment(double valor) {
        meioPagamento.doSomething(valor);
    }
}

请注意,我们现在取决于具体实施的发明的接口?通过这种方式,我们将能够在不关心实施每个带下划线的班级(钢坯,克朗和给定)

的情况下进行付款

让我们使用实现付款并使流程的课程进行测试:

public class Program {
    public static void main(String[] args) {
        PaymentInterface debito = new DebitoCartao();
        Payment pagamento = new Payment(debito);

        pagamento.doPayment(1200);
    }
}

Image description

现在在CRT中使用相同的羊毛:

public class Program {
    public static void main(String[] args) {
        PaymentInterface credito = new CartaoCredito();
        Payment pagamento = new Payment(credito);

        pagamento.doPayment(1200);
    }
}

Image description

Image description

记住,始终取决于抽象(接口),而不是实现(具体类)

在©the下一个:)