[C# em termos simples] Capítulo 4: Noções básicas de programação orientada a objetos: encapsulamento, herança e polimorfismo

Encapsulamento, herança e polimorfismo são conceitos centrais na programação orientada a objetos que são críticos para a construção de sistemas de software flexíveis, extensíveis e de fácil manutenção.
Encapsulamento (Encapsulamento) oculta detalhes de implementação interna encapsulando dados e operações relacionadas em uma classe e fornece uma interface pública para interagir com o exterior. O encapsulamento ajuda a proteger a integridade e a segurança dos dados, ao mesmo tempo que fornece boas abstrações que tornam o código mais fácil de entender e usar. O encapsulamento também pode suportar modularização de código e desenvolvimento em equipe, e cada módulo pode ser desenvolvido e testado de forma independente, o que melhora a capacidade de manutenção e reutilização do código.
Herança (Herança) permite que uma classe herde as propriedades e métodos de outra classe, de modo a conseguir a reutilização e extensão do código. A herança fornece uma estrutura hierárquica de código, para que as classes relacionadas possam ser organizadas juntas e o compartilhamento de código e interfaces unificadas possam ser realizados por meio de herança. A herança também pode suportar polimorfismo, reescrevendo os métodos da classe pai na subclasse para obter comportamentos diferentes de objetos diferentes.
Polimorfismo (Polimorfismo) permite que a mesma operação produza comportamentos diferentes em objetos diferentes. O polimorfismo fornece flexibilidade e extensibilidade, permitindo que o código lide com vários tipos de objetos sem escrever explicitamente códigos diferentes para cada tipo. O polimorfismo pode ser alcançado através da reescrita de métodos, sobrecarga de métodos e uso de interfaces, o que pode tornar o código mais flexível e extensível, ao mesmo tempo que melhora a legibilidade e a manutenção do código.

Dica: Encapsulamento, herança e polimorfismo são os conceitos centrais da programação orientada a objetos, que podem nos ajudar a construir sistemas de software mais flexíveis, escaláveis ​​e de fácil manutenção. Ao projetar e implementar código, a aplicação racional desses conceitos pode melhorar a qualidade, a reutilização e a escalabilidade do código para atender aos requisitos em constante mudança.

1. Embalagem

1.1 O conceito e finalidade do encapsulamento

O encapsulamento é um conceito importante na programação orientada a objetos. Refere-se ao encapsulamento de dados e operações relacionadas em uma classe, ocultando detalhes internos da implementação e expondo apenas as interfaces públicas necessárias para a interação de outros objetos. O objetivo do encapsulamento é proteger a integridade e a segurança dos dados e fornecer uma interface abstrata clara, tornando o código mais fácil de entender, usar e manter.
O objetivo principal do encapsulamento é o seguinte:

  1. Ocultação de dados: ao declarar dados como membros privados, apenas os métodos públicos da classe têm permissão para acessar e modificar os dados. Isto impede o acesso direto e a modificação dos dados, protegendo assim a consistência e a segurança dos dados.
  2. Implementar abstração: o encapsulamento permite que dados e operações complexas sejam abstraídas, fornecendo uma interface pública simples para uso de outros objetos. Ao ocultar detalhes internos de implementação, o encapsulamento pode ocultar lógica complexa, de modo que os usuários só precisem prestar atenção ao comportamento do objeto sem se preocupar com sua implementação interna.
  3. Reutilização de código: o encapsulamento pode encapsular um grupo de dados e operações relacionadas em uma classe, que pode realizar a reutilização de código. Outros objetos podem obter a mesma funcionalidade e comportamento criando uma instância desta classe, evitando duplicação de código semelhante.
  4. Melhorar a capacidade de manutenção: Através do encapsulamento, o estado e o comportamento dos objetos podem ser organizados em conjunto e uma boa interface abstrata pode ser fornecida. Desta forma, no processo subsequente de manutenção do código, a função do objeto pode ser modificada e estendida de forma mais conveniente, sem afetar outras partes.

Dica: O encapsulamento é um princípio importante da programação orientada a objetos. Ao encapsular dados e operações juntos, os dados podem ser ocultos e abstraídos, e a segurança, a reutilização e a manutenção do código podem ser melhoradas. A aplicação razoável do encapsulamento pode melhorar a confiabilidade e a legibilidade do código, ao mesmo tempo que reduz o acoplamento do código, para que o programa seja mais flexível e escalável.

1.2 A importância e os benefícios da ocultação de dados

A ocultação de dados é um dos principais conceitos de encapsulamento na programação orientada a objetos. Sua importância e benefícios se refletem nos seguintes aspectos:

  1. Proteja a integridade e a segurança dos dados: ao ocultar os dados dentro das classes e permitir apenas o acesso e a modificação dos dados por meio de interfaces públicas, os dados podem ser evitados de serem mal utilizados ou destruídos. Este mecanismo de controle de acesso pode garantir que os dados sejam operados corretamente em circunstâncias apropriadas, melhorando a integridade e a segurança dos dados.
  2. Realize modularização e dissociação: a ocultação de dados ajuda a realizar o design modular, oculta os detalhes de implementação dentro da classe e fornece apenas interfaces públicas limitadas. Dessa forma, ao utilizar uma classe, você só precisa estar atento ao uso da interface pública, e não precisa se preocupar com a implementação interna, que realiza o desacoplamento entre as classes.
  3. Simplifique a interface e melhore a legibilidade do código: a ocultação de dados pode encapsular detalhes complexos de implementação interna e fornecer uma interface concisa e clara para usuários externos. Dessa forma, o usuário só precisa estar atento ao método de chamada e aos parâmetros da interface, não precisando se preocupar com a implementação específica, o que melhora a legibilidade e compreensão do código.
  4. Promova a reutilização e manutenção de código: a ocultação de dados pode encapsular dados e operações relacionadas em uma classe para formar um módulo independente. Desta forma, outro código pode reutilizar a funcionalidade deste módulo criando uma instância da classe. Ao mesmo tempo, quando a função precisa ser modificada ou estendida, apenas a implementação interna da classe precisa ser modificada sem afetar os usuários externos, o que melhora a capacidade de manutenção e reutilização do código.
  5. Melhorar a confiabilidade e estabilidade do software: a ocultação de dados ajuda a reduzir o acesso direto aos dados e opera os dados por meio da interface pública da classe, reduzindo a possibilidade de erros. Isso pode melhorar a confiabilidade do software, evitar que os dados sejam modificados ou adulterados incorretamente e melhorar a estabilidade do software.

Dica: A ocultação de dados é um princípio de programação importante, pode proteger a integridade e segurança dos dados, realizar modularização e dissociação, simplificar interfaces e melhorar a legibilidade do código, promover a reutilização e manutenção do código e melhorar o desempenho do software, confiabilidade e estabilidade. Ao aplicar adequadamente a ocultação de dados, a capacidade de manutenção, a reutilização e a escalabilidade do código podem ser melhoradas, de modo a construir um sistema de software de maior qualidade.

1.3 Introdução aos modificadores de acesso e níveis de encapsulamento

Na programação orientada a objetos, modificadores de acesso são usados ​​para controlar a visibilidade e o acesso dos membros de uma classe (propriedades, métodos, campos, etc.) ao código externo. C# fornece os seguintes modificadores de acesso:

  1. público: Um modificador de acesso público, indicando que o membro está visível e acessível a qualquer código. Membros públicos podem ser acessados ​​por instâncias da classe, subclasses e outros códigos.
  2. private: modificador de acesso privado, indicando que o membro só pode ser acessado dentro da classe em que está definido. Membros privados são invisíveis para código externo e só podem ser usados ​​dentro da classe.
  3. protegido: Um modificador de acesso protegido, indicando que o membro é visível e acessível à classe na qual está definido e às subclasses dessa classe. Os membros protegidos são invisíveis para outros códigos.
  4. internal: Um modificador de acesso interno, indicando que o membro está visível e acessível para código no mesmo assembly. Um assembly é uma coleção de arquivos de código relacionados que podem consistir em um único projeto ou em vários projetos.
  5. interno protegido: O modificador de acesso interno protegido indica que o membro está visível e acessível ao código no mesmo assembly e às subclasses da classe.

Esses modificadores de acesso podem ser aplicados aos membros de uma classe para restringir o nível de acesso do membro conforme desejado. Ao escolher o modificador de acesso apropriado, você pode controlar a visibilidade dos membros da classe para o código externo e concretizar o conceito de encapsulamento.
O nível de encapsulamento refere-se à visibilidade dos membros dentro e fora da classe. De acordo com o nível de encapsulamento, os membros podem ser classificados nas seguintes categorias:

  1. Membros públicos (públicos): Esses membros são visíveis e acessíveis tanto ao código interno quanto externo da classe.
  2. Membros privados (private): Esses membros só são visíveis e acessíveis dentro da classe em que estão definidos e não são visíveis para código externo.
  3. Membros protegidos: Esses membros são visíveis e acessíveis dentro da classe em que estão definidos e nas subclasses dessa classe, mas não são visíveis para outro código.
  4. Membros internos (internos): Esses membros são visíveis e acessíveis para código no mesmo assembly, mas não visíveis para código em outros assemblies.
  5. membros internos protegidos: Esses membros são visíveis e acessíveis ao código no mesmo assembly e às subclasses da classe.

Ao selecionar modificadores de acesso e níveis de encapsulamento apropriados, o controle de acesso aos membros da classe pode ser alcançado, a integridade e a segurança dos dados podem ser protegidas e interfaces apropriadas podem ser fornecidas para uso de códigos externos. Isso pode atingir o objetivo de encapsulamento, ocultar os detalhes internos da classe e melhorar a capacidade de manutenção e reutilização do código.

1.4 Definição e uso de atributos

Uma propriedade é uma forma de acessar e manipular campos em uma classe, que permite o encapsulamento dos membros da classe para controlar o acesso e a modificação de seus dados. As propriedades fornecem uma maneira mais flexível e segura de acessar e manipular os dados de uma classe enquanto ocultam os detalhes reais dos dados.
Em C#, a definição e o uso de atributos incluem os seguintes aspectos:

  1. Definição de propriedades: As propriedades geralmente consistem em dois acessadores, um para obter o valor da propriedade (acessador get) e outro para definir o valor da propriedade (acessador set). Você pode usar getas setpalavras-chave e para definir o acessador da propriedade e implementar a lógica de leitura e atribuição da propriedade nele.
  2. Sintaxe para uma propriedade: A sintaxe de uma propriedade tem o formato 访问修饰符 数据类型 属性名称 { get; set; }, onde o modificador de acesso pode ser public, , privateetc., datatype especifica o tipo de dados da propriedade e propertyname é o nome que identifica a propriedade.
  3. Acesso e atribuição de atributos:. ao usar atributos, você pode usar o operador ponto para obter e definir o valor do atributo da mesma forma que acessa campos . Por exemplo, 对象.属性名称você pode obter o valor de um atributo e 对象.属性名称 = 值definir o valor de um atributo.
  4. Propriedades automáticas: se a lógica de leitura e atribuição de propriedades for relativamente simples, você poderá usar propriedades automáticas para simplificar o código. As propriedades automáticas usam um formato de sintaxe simplificado, sem necessidade de definir acessadores explicitamente, o compilador gerará automaticamente acessadores padrão para propriedades.
  5. Propriedades somente leitura: as propriedades somente leitura contêm apenas getacessadores para acesso somente leitura ao valor da propriedade. As propriedades somente leitura são inicializadas no momento da declaração e não podem ser modificadas.

O uso de atributos pode fornecer melhor encapsulamento e controle de acesso, tornando mais fácil para os usuários da classe obter e modificar os dados da classe, ao mesmo tempo que oculta os detalhes reais da implementação dos dados, melhorando a segurança e a manutenção do código. Os atributos também podem implementar validação e restrições nos valores dos atributos, aumentando o controle e o gerenciamento dos dados.

1.5 Definição e utilização de métodos

Um método é um bloco de código executável que executa uma ação específica ou uma tarefa específica. Na programação orientada a objetos, um método é um membro de uma classe que encapsula uma série de operações relacionadas para invocação e execução quando necessário.
Em C#, a definição e o uso de métodos incluem os seguintes aspectos:

  1. Definição do método: A definição de um método inclui o modificador de acesso do método, tipo de retorno, nome do método, lista de parâmetros e corpo do método. O modificador de acesso determina os direitos de acesso do método, o tipo de retorno especifica o tipo de dados retornado pelo método, o nome do método é o nome que identifica o método e a lista de parâmetros especifica os parâmetros aceitos pelo método.
  2. Sintaxe de um método: A forma gramatical de um método é 访问修饰符 返回类型 方法名称(参数列表) { 方法体 }, onde o modificador de acesso pode ser public, , privateetc., o tipo de retorno especifica o tipo de dados retornado pelo método, o nome do método é o nome que identifica o método e o parâmetro list contém os parâmetros aceitos pelo método.
  3. Chamada de método: ao usar um método, você pode chamá-lo por meio do nome do método e da lista de parâmetros e obter o valor de retorno do método (se houver). A invocação de método pode ser feita por nome de objeto ou de classe, dependendo se o método é um método de instância ou um método estático.
  4. Passagem de parâmetros do método: o método pode aceitar parâmetros, e os parâmetros são usados ​​para passar dados para o método. Os parâmetros podem ser tipos de valor ou tipos de referência e podem ser passados ​​por valor ou por referência.
  5. O valor de retorno do método: o método pode retornar um valor e o tipo do valor de retorno deve corresponder ao tipo de retorno do método. Use returninstruções para retornar resultados ao chamador.
  6. Sobrecarga de métodos: Em uma classe, vários métodos com o mesmo nome, mas listas de parâmetros diferentes, podem ser definidos, o que é chamado de sobrecarga de métodos. A sobrecarga de método pode executar lógicas diferentes com base em diferentes tipos e números de parâmetros.

Dica: O uso de métodos pode realizar a modularização e reutilização do código, dividir a lógica complexa em vários métodos pequenos e melhorar a legibilidade e a manutenção do código. O método também pode aceitar parâmetros e retornar valores, realizar transferência e processamento de dados e fornecer funções mais flexíveis. Através da sobrecarga de métodos, o mesmo nome de método pode ser utilizado de acordo com diferentes requisitos, o que aumenta a flexibilidade e escalabilidade do código.

2. Herança

2.1 O conceito e implementação da herança

Herança é um conceito importante na programação orientada a objetos, que permite que uma classe derive de outra classe para obter as propriedades e métodos da classe herdada. Em C#, a herança é obtida por meio de classdois pontos após a palavra-chave :. O conceito de herança pode ser ilustrado pelo seguinte exemplo de código:

class Animal // 基类 Animal
{
    
    
    public string Name {
    
     get; set; }

    public void Eat()
    {
    
    
        Console.WriteLine("Animal is eating.");
    }
}

class Dog : Animal // 派生类 Dog 继承自 Animal
{
    
    
    public void Bark()
    {
    
    
        Console.WriteLine("Dog is barking.");
    }
}

No exemplo acima, definimos uma classe base Animale uma classe derivada Dog. A classe derivada é designada Dogpor dois pontos :como classe base Animal, o que significa que Doga classe herda Animalas propriedades e métodos da classe.
Através da herança, as classes derivadas podem obter os membros públicos da classe base, como Namepropriedades e Eat()métodos. Além disso, as classes derivadas também podem adicionar suas próprias propriedades e métodos exclusivos, como Bark()métodos.
Por meio da herança, podemos conseguir a reutilização e extensão do código. As propriedades e métodos da classe base podem ser usados ​​diretamente na classe derivada sem reescrever. A classe derivada pode adicionar novas funções com base na classe base, tornando o código mais flexível e extensível.

Dica: C# não suporta herança múltipla, uma classe só pode herdar de uma classe base. No entanto, as funções de múltiplas interfaces podem ser implementadas através da interface para obter um efeito semelhante à herança múltipla.

2.2 A diferença entre herança única e herança múltipla

Herança única e herança múltipla são duas formas diferentes de herança na programação orientada a objetos e existem algumas diferenças entre elas.
Herança única significa que uma classe só pode herdar de uma classe base. Na herança única, uma classe derivada pode ter apenas uma classe base direta. Essa restrição torna a hierarquia de classes mais simples e clara. A herança única ajuda a reduzir a complexidade do código e os custos de manutenção. C# é uma linguagem de herança única, uma classe só pode herdar de uma classe base, mas pode implementar múltiplas interfaces.
Herança múltipla significa que uma classe pode herdar de múltiplas classes base. Na herança múltipla, uma classe derivada pode ter várias classes base diretas. A herança múltipla pode aumentar a flexibilidade e a reutilização do código até certo ponto, porque uma classe pode herdar as propriedades e métodos de várias classes base diferentes. Contudo, a herança múltipla também traz alguns problemas, como conflitos de nomenclatura e ambiguidade semântica. Para evitar esses problemas, algumas linguagens de programação (como C#) optam por não suportar herança múltipla, mas implementam funções semelhantes à herança múltipla por meio de interfaces.
Resumindo, a herança única é o principal método de herança em C#, que fornece uma hierarquia de classes simples e clara. A herança múltipla pode fornecer maior flexibilidade e reutilização em alguns casos, mas também aumenta a complexidade do código e problemas potenciais. Em C#, o efeito semelhante à herança múltipla pode ser alcançado através da interface, e os problemas que podem ser causados ​​pela herança múltipla podem ser evitados.

2.3 Vantagens e Cenários de Aplicação da Herança

A herança tem muitas vantagens e cenários de aplicação na programação orientada a objetos:

  1. Reutilização de código: a herança permite que as subclasses herdem as propriedades e métodos da classe pai, conseguindo assim a reutilização de código. A subclasse pode usar diretamente os membros da classe pai sem reescrever o mesmo código, o que melhora a capacidade de reutilização e a eficiência do código.
  2. Hierarquia de herança: A herança suporta a criação de hierarquias. Através do relacionamento de herança de classes derivadas, uma hierarquia de classes ordenada pode ser formada. Essa hierarquia torna o código mais claro, fácil de organizar e manter.
  3. Polimorfismo: A herança é a base do polimorfismo. Através da herança, as subclasses podem substituir os métodos da classe pai ou adicionar seus próprios métodos, alcançando assim o polimorfismo. O polimorfismo permite que o mesmo método se comporte de maneira diferente em objetos diferentes, aumentando a flexibilidade e escalabilidade do código.
  4. Extensão e customização: Através da herança, extensões e customizações podem ser feitas com base nas classes existentes. As subclasses podem adicionar novas propriedades e métodos ou modificar o comportamento da classe pai para atender a necessidades específicas. Essa flexibilidade faz com que a herança tenha grande valor de aplicação no desenvolvimento de software.

Os cenários de aplicativos herdados incluem, entre outros, os seguintes aspectos:

  1. Generalidade e especialização: por meio da herança, você pode criar uma classe pai geral e, em seguida, derivar subclasses específicas para atender a diferentes necessidades. Por exemplo, você pode criar uma classe genérica "Animal" e então derivar subclasses específicas como "Cachorro" e "Gato".
  2. Extensão e customização: Através da herança, as classes existentes podem ser estendidas e customizadas para atender necessidades específicas. Por exemplo, você pode criar uma classe de formulário básica e, em seguida, derivar subclasses específicas, como "formulário principal", "formulário de diálogo" etc., para personalizar diferentes tipos de formulários.
  3. Interfaces e implementações: Ao herdar interfaces, você pode definir um conjunto de especificações comportamentais compartilhadas e implementar essas interfaces em classes concretas. Isso pode atingir um efeito semelhante à herança múltipla, proporcionando maior flexibilidade e capacidade de reutilização de código.

Dica: Herança é um conceito importante na programação orientada a objetos, pois fornece flexibilidade e escalabilidade por meio da reutilização de código, hierarquia, polimorfismo e escalabilidade, tornando o desenvolvimento de software mais eficiente e sustentável. No cenário certo, o uso razoável da herança pode melhorar a qualidade e a reutilização do código.

Três, polimorfismo

3.1 O conceito e características do polimorfismo

Polimorfismo é um conceito importante na programação orientada a objetos, que se refere ao mesmo método mostrando comportamentos diferentes em objetos diferentes. O polimorfismo nos permite usar uma interface unificada para lidar com diferentes tipos de objetos sem prestar atenção ao tipo de objetos específicos, melhorando assim a flexibilidade e escalabilidade do código. As características do polimorfismo incluem:

  1. Reescrita de métodos: o polimorfismo é baseado no conceito de herança, e as subclasses podem reescrever os métodos da classe pai, alterando assim a implementação específica do método. Quando este método for chamado, de acordo com o tipo do objeto real, será executado o método da subclasse correspondente.
  2. Desempenho diferente do mesmo método: Através do polimorfismo, o mesmo método pode ser chamado em objetos diferentes, mas a implementação específica de cada objeto pode ser diferente. Isso permite que o código execute diferentes comportamentos dependendo do tipo de objeto, proporcionando maior flexibilidade e extensibilidade.
  3. Determinação em tempo de execução: O comportamento específico do polimorfismo é determinado dinamicamente em tempo de execução, não em tempo de compilação. Isso significa que o compilador não sabe qual método do objeto está sendo chamado em tempo de compilação, mas executa a ligação dinâmica de acordo com o tipo do objeto real quando o programa está em execução.
  4. Reutilização de código: O polimorfismo possibilita processar diferentes tipos de objetos por meio de uma interface comum, realizando assim a reutilização de código. Você pode escrever métodos ou classes comuns e depois usar polimorfismo para lidar com diferentes tipos de objetos, reduzindo a duplicação de código.
  5. Extensibilidade: Através de herança e polimorfismo, novas subclasses podem ser criadas com base em classes existentes, e métodos podem ser reescritos ou adicionados para estender as funções originais. Essa extensibilidade torna os programas mais fáceis de manter e expandir.
3.2 Maneiras de alcançar polimorfismo
  1. Substituição de métodos
    Na programação orientada a objetos, o polimorfismo pode ser alcançado por meio da substituição de métodos. A substituição de método significa que a subclasse reescreve o método da classe pai, alterando assim a implementação específica do método. Através da reescrita de métodos, o mesmo método pode ser chamado em objetos diferentes, mas a implementação específica de cada objeto pode ser diferente, alcançando assim o polimorfismo.
    Aqui está um exemplo de código que mostra como o polimorfismo pode ser alcançado através da substituição de métodos:
    class Animal
    {
          
          
        public virtual void MakeSound()                                        
        {
          
          
            Console.WriteLine("The animal makes a sound");
        }
    }
    
    class Dog : Animal
    {
          
          
        public override void MakeSound()
        {
          
          
            Console.WriteLine("The dog barks");
        }
    }
    
    class Cat : Animal
    {
          
          
        public override void MakeSound()
        {
          
          
            Console.WriteLine("The cat meows");
        }
    }
    
    class Program
    {
          
          
        static void Main(string[] args)
        {
          
          
            Animal animal1 = new Animal();
            Animal animal2 = new Dog();
            Animal animal3 = new Cat();
    
            animal1.MakeSound();  // Output: "The animal makes a sound"
            animal2.MakeSound();  // Output: "The dog barks"
            animal3.MakeSound();  // Output: "The cat meows"
        }
    }
    
    No exemplo acima, Animalclass é uma classe base Doge Catclass é Animaluma subclasse que herda de class. Todos eles substituem MakeSoundo método para produzir sons diferentes, respectivamente. No método, Mainé criada uma Animalinstância da classe animal1, bem como instâncias das duas subclasses animal2e animal3. Ao chamar seus MakeSoundmétodos, você pode ver diferentes resultados de saída, ou seja, a implementação específica de diferentes objetos. Aqui, através da reescrita de métodos, é realizado o polimorfismo do mesmo método em objetos diferentes. De acordo com o tipo do objeto real, o método da subclasse correspondente é chamado, realizando assim diferentes comportamentos de diferentes objetos. Esta é uma implementação do polimorfismo.
  2. Sobrecarga de métodos
    Na programação orientada a objetos, a sobrecarga de métodos é outra maneira de obter polimorfismo. A sobrecarga de métodos refere-se à definição de vários métodos na mesma classe com o mesmo nome, mas com listas de parâmetros diferentes. Aqui está um exemplo de código que mostra como obter polimorfismo por meio da sobrecarga de métodos:
    class Calculator 
    {
          
          
        public int Add(int num1, int num2)
        {
          
          
            return num1 + num2;
        }
    
        public double Add(double num1, double num2)
        {
          
          
            return num1 + num2;
        }
    }
    
    class Program
    {
          
          
        static void Main(string[] args)
        {
          
          
            Calculator calculator = new Calculator();
    
            int result1 = calculator.Add(10, 5);
            Console.WriteLine("Result 1: " + result1);  // Output: 15
    
            double result2 = calculator.Add(3.14, 2.71);
            Console.WriteLine("Result 2: " + result2);  // Output: 5.85
        }
    }
    
    No exemplo acima, Calculatora classe define dois Addmétodos, um aceitando dois parâmetros inteiros e outro aceitando dois parâmetros do tipo duplo. Os dois métodos têm o mesmo nome, mas listas de parâmetros diferentes, isso é sobrecarga de método. No método, Mainé criada uma Calculatorinstância da classe calculator. Chamando Addo método e passando diferentes tipos de parâmetros, você pode ver diferentes resultados de saída.
    Através da sobrecarga de método, você pode selecionar o método correspondente a ser chamado de acordo com o tipo do parâmetro. Desta forma, vários métodos com o mesmo nome, mas listas de parâmetros diferentes, são definidos na mesma classe, alcançando o polimorfismo. Dependendo do tipo do parâmetro real, o método correspondente é chamado para obter comportamentos diferentes. Esta é uma maneira pela qual a sobrecarga do método atinge o polimorfismo.
  3. Interfaces e classes abstratas
    Interfaces são uma forma de alcançar polimorfismo definindo a especificação de um conjunto de métodos e propriedades sem conter uma implementação concreta. Uma classe pode implementar uma ou mais interfaces e fornecer implementações concretas dos métodos e propriedades definidas nas interfaces. Aqui está um exemplo de código que implementa polimorfismo usando interfaces:
    // 定义一个接口
    public interface IShape 
    {
          
          
        double CalculateArea();
    }
    
    // 实现接口的类
    public class Circle : IShape
    {
          
          
        private double radius;
    
        public Circle(double radius)
        {
          
          
            this.radius = radius;
        }
    
        public double CalculateArea()
        {
          
          
            return Math.PI * radius * radius;
        }
    }
    
    public class Rectangle : IShape
    {
          
          
        private double width;
        private double height;
    
        public Rectangle(double width, double height)
        {
          
          
            this.width = width;
            this.height = height;
        }
    
        public double CalculateArea()
        {
          
          
            return width * height;
        }
    }
    
    // 使用接口实现多态性
    public class Program
    {
          
          
        static void Main(string[] args)
        {
          
          
            // 创建不同的形状对象
            IShape circle = new Circle(5);
            IShape rectangle = new Rectangle(4, 6);
    
            // 调用统一的接口方法
            double circleArea = circle.CalculateArea();
            double rectangleArea = rectangle.CalculateArea();
    
            Console.WriteLine("Circle Area: " + circleArea);
            Console.WriteLine("Rectangle Area: " + rectangleArea);
        }
    }
    
    No exemplo acima, definimos uma IShapeinterface, que contém CalculateAreaa definição do método. Em seguida, criamos classes Circlee Rectangle, que implementam respectivamente IShapea interface e fornecem CalculateAreaimplementações concretas do método. No método Programda classe Main, criamos instâncias de Circlee Rectanglee chamamos seus CalculateAreamétodos através de variáveis ​​de interface, realizando o polimorfismo.
    A classe abstrata também é uma forma de obter polimorfismo, pode conter métodos abstratos e métodos concretos. Uma classe abstrata em si não pode ser instanciada, mas o polimorfismo pode ser alcançado herdando suas subclasses. A seguir está um exemplo de código para implementar polimorfismo usando classes abstratas:
    // 定义一个抽象类
    public abstract class Shape 
    {
          
          
        public abstract double CalculateArea();
    }
    
    // 继承抽象类的子类
    public class Circle : Shape
    {
          
          
        private double radius;
    
        public Circle(double radius)
        {
          
          
            this.radius = radius;
        }
    
        public override double CalculateArea()
        {
          
          
            return Math.PI * radius * radius;
        }
    }
    
    public class Rectangle : Shape
    {
          
          
        private double width;
        private double height;
    
        public Rectangle(double width, double height)
        {
          
          
            this.width = width;
            this.height = height;
        }
    
        public override double CalculateArea()
        {
          
          
            return width * height;
        }
    }
    
    // 使用抽象类实现多态性
    public class Program
    {
          
          
        static void Main(string[] args)
        {
          
          
            // 创建不同的形状对象
            Shape circle = new Circle(5);
            Shape rectangle = new Rectangle(4, 6);
    
            //
    
     调用统一的抽象方法
            double circleArea = circle.CalculateArea();
            double rectangleArea = rectangle.CalculateArea();
    
            Console.WriteLine("Circle Area: " + circleArea);
            Console.WriteLine("Rectangle Area: " + rectangleArea);
        }
    }
    
    No exemplo acima, definimos uma classe abstrata Shapeque contém métodos abstratos CalculateArea. Em seguida, criamos classes Circlee Rectangle, que herdam da Shapeclasse abstrata e fornecem CalculateAreaimplementações concretas do método. No método Programda classe Main, criamos instâncias de Circlee Rectanglee chamamos seus CalculateAreamétodos através de variáveis ​​de classe abstratas, realizando o polimorfismo. Tanto as interfaces quanto as classes abstratas são formas eficazes de obter polimorfismo, e você pode escolher uma forma adequada de projetar e implementar código de acordo com necessidades específicas.
3.3 Vantagens e Cenários de Aplicação do Polimorfismo

O polimorfismo apresenta vantagens importantes e uma ampla gama de cenários de aplicação na programação orientada a objetos, refletidos principalmente nos seguintes aspectos:

  1. Flexibilidade e extensibilidade : O polimorfismo permite a utilização de variáveis ​​de classe base ou tipo de interface para se referir a objetos de subclasses ou classes de implementação, tornando o código mais flexível e extensível. Através do polimorfismo, podemos facilmente adicionar novas subclasses ou classes de implementação sem modificar o código existente, e esses objetos podem ser manipulados através de referências de classe base ou tipos de interface.
  2. Reutilização de código : o polimorfismo nos permite definir operações comuns na classe base ou interface sem escrever o mesmo código repetidamente em cada subclasse ou classe de implementação. Isso pode melhorar a reutilização do código e reduzir a redundância do código.
  3. Simplifique a lógica do código : Através do polimorfismo, o tipo concreto do objeto pode ser escondido atrás da interface ou classe base, simplificando assim a lógica do código. Precisamos apenas nos concentrar no comportamento e nos métodos do objeto, não no tipo específico do objeto.
  4. Substituibilidade e testabilidade : o polimorfismo nos permite tratar objetos como instâncias de suas classes base ou interfaces, o que facilita a substituição e o teste de código. Podemos facilmente substituir um objeto de uma subclasse ou classe de implementação por outra, apenas certifique-se de que ambos implementem a mesma interface ou herdem a mesma classe base.
  5. Programação orientada à abstração : O polimorfismo incentiva a ideia de programação orientada à abstração, ou seja, focando no comportamento e nas funções dos objetos, em vez de focar nos detalhes específicos de implementação dos objetos. Isso melhora a legibilidade e a manutenção do código.

Cenário de aplicação:

  • Nos padrões de projeto, o polimorfismo é a base de muitos padrões de projeto, como o padrão de fábrica, o padrão de estratégia e o padrão de observador.
  • Na programação GUI, o polimorfismo pode ser usado para lidar com a interação do usuário e manipulação de eventos.
  • Nas operações de banco de dados, o polimorfismo pode ser usado para lidar com diferentes tipos de objetos de dados.
  • No desenvolvimento de frameworks e bibliotecas, o polimorfismo pode fornecer interfaces estendidas e customizadas.

4. Exemplos de encapsulamento, herança e polimorfismo

4.1 Como encapsular as propriedades e métodos de uma classe

O encapsulamento é um dos conceitos centrais da programação orientada a objetos. Ele encapsula as propriedades e métodos de uma classe, oculta detalhes internos da implementação e expõe apenas as interfaces necessárias para acesso e operação externa. A seguir estão formas comuns e códigos de exemplo para encapsular propriedades e métodos de classe:
encapsular propriedades de classe:

  1. Use campos privados e propriedades públicas:

    public class Person 
    {
          
          
        private string name; // 私有字段
    
        public string Name // 公共属性
        {
          
          
            get {
          
           return name; }
            set {
          
           name = value; }
        }
    }
    
  2. Use propriedades automáticas:

    public class Person 
    {
          
          
        public string Name {
          
           get; set; } // 自动属性
    }
    

Método de classe de encapsulamento:

  1. Use o método público:
    public class Calculator   
    {
          
          
        public int Add(int a, int b)
        {
          
          
            return a + b;
        }
    }
    
  2. Usando métodos privados e públicos:
    public class Calculator  
    {
          
          
        private int Multiply(int a, int b)
        {
          
          
            return a * b;
        }
    
        public int Calculate(int a, int b)
        {
          
          
            int result = Multiply(a, b); // 调用私有方法
            return result;
        }
    }
    

Os benefícios de encapsular propriedades e métodos de classe incluem:

  • Ocultação e segurança de dados : o encapsulamento pode ocultar os detalhes internos de implementação de uma classe e expor apenas as interfaces necessárias, melhorando assim a segurança e a confiabilidade dos dados.
  • Reutilização e capacidade de manutenção de código : por meio do encapsulamento, funções comuns podem ser encapsuladas em métodos e reutilizadas quando necessário, reduzindo a escrita repetitiva de código e melhorando a capacidade de manutenção e legibilidade do código.
  • Uniformidade da interface : Através do encapsulamento, uma interface unificada pode ser definida, de forma que os usuários da classe só precisem prestar atenção em como utilizar as funções fornecidas pela interface, e não precisem se preocupar com detalhes específicos de implementação, o que reduz o acoplamento do código.
4.2 Uso de herança e construção de cadeia de herança

Herança é um conceito importante na programação orientada a objetos, que permite que uma classe (subclasse) herde as propriedades e métodos de outra classe (classe pai) e possa adicionar suas próprias funções extras. A seguir estão o uso básico da herança e como construir a cadeia de herança, bem como o código de exemplo correspondente:
uso da herança:

  1. Defina a classe pai:
    public class Vehicle  
    {
          
          
        public void Start()
        {
          
          
            Console.WriteLine("Vehicle starts.");
        }
    
        public void Stop()
        {
          
          
            Console.WriteLine("Vehicle stops.");
        }
    }
    
  2. Defina uma subclasse e herde a classe pai:
    public class Car : Vehicle
    {
          
          
        public void Accelerate()
        {
          
          
            Console.WriteLine("Car accelerates.");
        }
    
        public void Brake()
        {
          
          
            Console.WriteLine("Car brakes.");
        }
    }
    
  3. Crie um objeto de subclasse e chame os métodos da classe pai e da subclasse:
    Car car = new Car(); 
    car.Start(); // 调用父类的方法
    car.Accelerate(); // 调用子类的方法
    car.Stop(); // 调用父类的方法
    

Construção da cadeia de herança:
a herança pode formar um relacionamento de herança multinível e construir uma cadeia de herança para que as subclasses possam herdar as propriedades e métodos da classe pai e passá-los para as subclasses de nível inferior. Por exemplo:

public class Animal
{
    
    
    public void Eat()  
    {
    
    
        Console.WriteLine("Animal eats.");
    }
}

public class Mammal : Animal
{
    
    
    public void Walk()
    {
    
    
        Console.WriteLine("Mammal walks.");
    }
}

public class Dog : Mammal
{
    
    
    public void Bark()
    {
    
    
        Console.WriteLine("Dog barks.");
    }
}

No código acima, Doga classe herda da Mammalclasse e Mammala classe herda Animalda classe, formando uma cadeia de herança. Portanto, Doga classe pode acessar os métodos da Mammalclasse e da classe, alcançando o efeito de herança multinível.Animal

As vantagens e cenários de aplicação da herança incluem:

  • Reutilização de código : Por meio da herança, as subclasses podem reutilizar as propriedades e métodos da classe pai, evitando a escrita repetida de códigos semelhantes e melhorando a reutilização do código.
  • Funções estendidas : as subclasses podem adicionar suas próprias funções extras com base na herança da classe pai para realizar a extensão e personalização das funções.
  • Polimorfismo : Através da herança, o polimorfismo pode ser alcançado, ou seja, o mesmo método possui diferentes implementações em diferentes subclasses, proporcionando um método de programação mais flexível e extensível.
4.3 Aplicação do polimorfismo

Polimorfismo é um conceito importante de programação orientada a objetos, que permite o uso de variáveis ​​do tipo de classe base para se referir a objetos da classe derivada e chamar o método correspondente de acordo com o tipo de objeto real. A aplicação do polimorfismo inclui principalmente a reescrita de métodos e o uso de interface.
Substituição de método:
a substituição de método refere-se à reimplementação de métodos existentes na classe pai na classe derivada. A substituição de método permite que a implementação de um método seja modificada de acordo com as necessidades da classe derivada para atender às necessidades específicas da classe derivada. A substituição de método requer que o nome do método, a lista de parâmetros e o tipo de retorno sejam iguais aos do método na classe pai.
O código de exemplo é o seguinte:

public class Shape  
{
    
    
    public virtual void Draw()
    {
    
    
        Console.WriteLine("Drawing a shape.");
    }
}

public class Circle : Shape
{
    
    
    public override void Draw()
    {
    
    
        Console.WriteLine("Drawing a circle.");
    }
}

public class Rectangle : Shape
{
    
    
    public override void Draw()
    {
    
    
        Console.WriteLine("Drawing a rectangle.");
    }
}

// 使用多态性调用方法
Shape shape = new Circle();
shape.Draw(); // 调用 Circle 类中的 Draw 方法

shape = new Rectangle();
shape.Draw(); // 调用 Rectangle 类中的 Draw 方法

No código acima, Shapea classe define um método virtual Draw()e as classes derivadas Circlesubstituem Rectangleesse método, respectivamente. Ao usar a variável do tipo de classe base para se referir ao objeto da classe derivada, o Draw()método correspondente pode ser chamado de acordo com o tipo de objeto real e o polimorfismo é realizado.
Uso de interfaces:
Uma interface é um protocolo que define um conjunto de métodos, propriedades e eventos e representa uma capacidade ou comportamento. Uma classe pode implementar uma ou mais interfaces e fornecer implementações para todos os membros definidos pelas interfaces. As interfaces permitem o compartilhamento e o comportamento consistente entre várias classes.
O código de exemplo é o seguinte:

public interface IPlayable  
{
    
    
    void Play();
}

public class VideoPlayer : IPlayable
{
    
    
    public void Play()
    {
    
    
        Console.WriteLine("Playing video.");
    }
}

public class AudioPlayer : IPlayable
{
    
    
    public void Play()
    {
    
    
        Console.WriteLine("Playing audio.");
    }
}

// 使用多态性调用接口方法
IPlayable player = new VideoPlayer();
player.Play(); // 调用 VideoPlayer 类中实现的 Play 方法

player = new AudioPlayer();
player.Play(); // 调用 AudioPlayer 类中实现的 Play 方法

No código acima, IPlayableé uma interface VideoPlayere AudioPlayera classe implementa essa interface respectivamente. Ao declarar uma variável de um tipo de interface, você pode fazer referência a objetos de qualquer classe que implemente a interface e chamar métodos definidos pela interface. Desta forma, esses objetos podem ser operados uniformemente sem se preocupar com o tipo específico de objeto, e o polimorfismo é realizado.
A aplicação do polimorfismo pode melhorar a flexibilidade, escalabilidade e capacidade de manutenção do código. Através da reescrita de métodos e do uso de interfaces, a operação unificada de diferentes objetos pode ser realizada com base na herança e implementação, e a reutilização e escalabilidade do código podem ser aumentadas. Ao mesmo tempo, o polimorfismo também torna o código mais fácil de entender e manter, reduzindo códigos repetidos e julgamentos lógicos.

5. Precauções e melhores práticas

Ao usar encapsulamento, herança e polimorfismo, existem algumas considerações e práticas recomendadas que podem ajudar os desenvolvedores a escrever código orientado a objetos de alta qualidade:

  1. Considerações sobre embalagem e práticas recomendadas:
    • Ao encapsular dados, use campos privados e propriedades públicas para controlar o acesso aos dados.
    • Sempre que possível, torne os campos somente leitura ou use o atributo somente leitura para evitar modificação acidental de dados.
    • Siga o princípio do encapsulamento e encapsule dados e comportamentos relacionados na mesma classe para melhorar a legibilidade e a manutenção do código.
    • Evite o encapsulamento excessivo e encapsule apenas os dados e métodos necessários para evitar estruturas de código excessivamente complexas.
  2. Notas e práticas recomendadas para herança:
    • Use herança para conseguir compartilhamento e reutilização entre classes, mas siga o princípio da herança moderada para evitar herança muito profunda ou muito complicada.
    • Considere o Princípio de Responsabilidade Única para classes e garanta que os relacionamentos de herança correspondam aos relacionamentos de objetos do mundo real.
    • Use classes ou interfaces abstratas para definir o comportamento compartilhado, em vez de depender apenas da herança de classes concretas.
    • Use herança múltipla com moderação para evitar a introdução de complexidade e acoplamento desnecessário.
  3. Notas e práticas recomendadas para polimorfismo:
    • As interfaces são preferidas para polimorfismo porque fornecem acoplamento mais flexível e maior flexibilidade.
    • Tente usar classes ou interfaces abstratas como parâmetros de métodos ou tipos de retorno para receber mais objetos de tipos diferentes.
    • Ao substituir métodos, siga o contrato do método da classe base e certifique-se de fornecer uma implementação adequada na classe derivada.
    • Evite usar métodos de implementação concretos em classes base, para não quebrar o polimorfismo.
  4. Considerações gerais e melhores práticas:
    • Siga os princípios de design orientado a objetos, como princípio de responsabilidade única, princípio aberto-fechado, princípio de substituição de Liskov, etc.
    • Tente usar composição em vez de herança para evitar relacionamentos de herança excessivamente complexos e rígidos.
    • Mantenha seu código limpo e legível, evite design excessivo e hierarquias complexas.
    • Use nomes e comentários adequados para melhorar a compreensão e a manutenção do código.
    • Aplique padrões de design e práticas recomendadas para resolver problemas comuns de programação orientada a objetos.

6. Resumo

Na programação orientada a objetos, encapsulamento, herança e polimorfismo são três conceitos centrais que desempenham um papel importante no projeto e implementação de sistemas de software.
O encapsulamento consiste em encapsular dados e comportamentos em classes. Ao definir interfaces públicas e ocultar detalhes internos de implementação, ele fornece controle e proteção de acesso aos dados e melhora a manutenção e segurança do código.
A herança nos permite criar novas classes com base em classes existentes e realizar a reutilização e extensão de código herdando as propriedades e métodos das classes pai. Através da herança, podemos estabelecer um relacionamento hierárquico entre classes e adicionar novas funções em subclasses ou substituir o comportamento das classes pai.
O polimorfismo nos permite utilizar uma interface unificada para lidar com diferentes tipos de objetos, proporcionando flexibilidade e escalabilidade de código. Através do polimorfismo, podemos determinar o tipo real do objeto em tempo de execução e chamar o método correspondente de acordo com o tipo do objeto.
A combinação de encapsulamento, herança e polimorfismo torna a programação orientada a objetos altamente modular, flexível e de fácil manutenção. O uso razoável deles pode melhorar a legibilidade, escalabilidade e reutilização do código e reduzir a complexidade e o acoplamento do código.
Ao usar encapsulamento, herança e polimorfismo, você precisa seguir algumas práticas recomendadas e princípios de design, como o princípio de responsabilidade única, o princípio aberto-fechado, o princípio de substituição de Liskov, etc., para garantir a qualidade e a manutenibilidade do código .

Acho que você gosta

Origin blog.csdn.net/gangzhucoll/article/details/131343253
Recomendado
Clasificación