在软件的一生中,软件开发的大部分成本都用于维护软件。因此,作为开发人员,我们希望构建一些不皱眉而易于维护的东西。并且使用一些坚实的规则可以帮助我们实现这一目标
单一的责任原则
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());
}
}
最后,另一个呼叫这两个类的班级。
操作的演示:
arquivovoment persidit:
开放关闭原则
这是一个经过深入讨论的原则。他说,必须关闭课程才能开放以进行扩展。这意味着一类完成后,不应修改它而是继承,这会产生一些不适,因为我们无法更改我们的课程?遵循此原则的方法是使用抽象,抽象类甚至接口。我将展示两种使用此原则的方法。
可能您已经在生活中创建了一个计算器,在此计算器中应该是:
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。这不会在我们的班级中移动,该班级具有业务规则,过滤,尊重原则。操作的示例:
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作为第二个参数传递给了我们的过滤器。
成本结果:
Liskov替代原则
Liskov替换的原理决定了派生类必须由其基类代替。
在实践中,所有女儿(通过脚跟实施)必须保持与父级相同的行为(阶级是继承)。
让我们假设我们有一个人班,带有CPF,RG,名字和一个与人与母亲的人相同属性的学生班,并意识到我们可以让学生继承一个人? P>
但是,我们必须注意不要违反这一原则,尤其是在以下情况下:
- 哭泣/实施一个不做任何事情的母亲。
- 返回父亲班的不同类型和价值。
例如,如果在父类中,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和其他社交网络无法实施,因为母亲的数量©所有资源中的所有母亲© P>
我们将隔离界面,使停止更美丽和可用。
首先,让我们拿出这个中介类,创建一个称为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);
}
}
现在在CRT中使用相同的羊毛:
public class Program {
public static void main(String[] args) {
PaymentInterface credito = new CartaoCredito();
Payment pagamento = new Payment(credito);
pagamento.doPayment(1200);
}
}
记住,始终取决于抽象(接口),而不是实现(具体类)
在©the下一个:)