Este artigo foi traduzido por máquina.

O trabalho programador

Multiparadigmatic .net, parte 2

Ted Neward

Em meu artigo anterior (msdn.microsoft.com/magazine/ff955611 de ), a primeira desta série, mencionei que as duas linguagens centrais para o Microsoft .NET Framework — c# e Visual Basic — são linguagens multiparadigm, assim como a C++, seus sintáticas (no caso do c#) ou conceitual (no caso do Visual Basic) predecessora. Usando uma linguagem multiparadigmatic pode ser confuso e difícil, especialmente quando as finalidades dos paradigmas diferentes não criptografadas.

Aspectos comuns e variabilidade

Mas, antes, podemos começar levando-se entre os paradigmas diferentes nesses idiomas, uma pergunta maior vem à mente: O que, precisamente, são estamos tentando fazer quando criamos um sistema de software? Esqueça as metas “ resultado final ” – modularidade, extensibilidade, simplicidade e todos os que jazz — para um momento e concentrar-se mais informações sobre a “ como ” da linguagem. Como fazer isso, exatamente, estão estamos tentando criar todas essas metas “ resultado final ”?

James O. Coplien — do seu “ Multi-Paradigm Design para C++ ” (Addison-Wesley Professional, 1998) — tem uma resposta para nós:

Quando pensamos abstractly, podemos Enfatize o que é comum ao mesmo tempo em que a supressão de detalhes. Uma abstração de um bom software requer que compreendemos o problema bem suficiente em todos os seu amplitude para saber o que é comum em itens relacionados de seu interesse e saber quais detalhes variam de item a item. Os itens de interesse são coletivamente chamados de uma família de produtos e famílias — em vez de aplicativos individuais — são o escopo da arquitetura e design. Podemos usar o modelo de semelhança/variabilidade independentemente dos membros da família serem módulos, classes, funções, processos ou tipos;Ele funciona para qualquer paradigma. Aspectos comuns e variabilidade são a essência da maioria das técnicas de design.

Pense um pouco sobre o paradigma do objeto tradicional. Como desenvolvedores e orientada a objeto, podemos estiver ensinados no desde o início “ identificar os substantivos ” no sistema e procurar os itens que compõem uma determinada entidade — para localizar todos os itens pertencentes ao que está sendo “ professor ” dentro do sistema, por exemplo e coloque-as em uma classe chamada professores. Mas se várias “ substantivos ” têm um comportamento de sobreposição e relacionado — tais como aluno “ ” ter alguns dados e as operações sobrepostas com uma pessoa “ ”, mas com algumas marcadas diferenças — e em seguida, nós estiver ensinou que, em vez de replicar o código comum, deve elevar as características comuns em uma classe base e os tipos estão relacionados uns aos outros por meio de herança. Em outras palavras, elementos comuns são coletados juntos dentro de uma classe e variabilidade é capturada pelo estende a partir dessa classe e introduzir as variações. Localizar as semelhanças e variabilities dentro de um sistema e expressá-las, o coração do design de formulários.

Semelhanças com freqüência são as partes que são difíceis de identificar explicitamente, não porque nós não reconhecê-las, mas porque elas são tão facilmente e intuitivamente reconhecível é difícil identificá-los. Por exemplo, se eu digo, “ veículo ” que imagem é exibida em sua cabeça? Se fizermos este exercício com um grupo de pessoas, cada uma terá uma imagem diferente, ainda será grande a semelhança entre todos eles. No entanto, se começar listando vários veículos imaginados, os diferentes tipos de variabilities começam a surgir e categorizar propriamente ditos (esperamos), de modo que ainda temos um conjunto de semelhanças entre veículos.

Positivo e negativa variabilidade

Variabilidade pode vir de duas formas básicas, um dos quais é fácil de reconhecer e a outros muito mais difícil. Variação positiva é quando a variabilidade ocorre na forma de adicionar a semelhança básica. Por exemplo, imagine a abstração desejada é o de uma mensagem, como, por exemplo, uma mensagem SOAP ou email. Se decidimos que um tipo de mensagem tem um cabeçalho e corpo e deixar os diferentes tipos de mensagens para usar do que como as características comuns, em seguida, uma variação positiva sobre isso é uma mensagem que leve a um determinado valor em seu cabeçalho, talvez a data/hora ele foi enviado. Isso é geralmente facilmente capturado em construções de linguagem — no paradigma orientado a objeto, por exemplo, é relativamente trivial para criar uma subclasse de mensagem que adiciona o suporte para data/hora de envio.

Variação negativa, contudo, é muito mais complicada. Como pode ser deduzido, uma variação negativa remove ou contradiz algum aspecto das características comuns — uma mensagem que possui um cabeçalho, mas não há corpo (como, por exemplo, uma mensagem de confirmação de recebimento usada por infra-estrutura de mensagens) é uma forma de variabilidade negativa. E, como você provavelmente já pode imaginar, isso a captura de uma construção de linguagem é problemática — c# nem Visual Basic tem um recurso para remover um membro declarado na classe base. Nesse caso, o melhor a que fazer é retorno nulo ou nada do membro body, que será reproduzido claramente estragos com qualquer código que espera um corpo deve estar presente, como, por exemplo, as rotinas de verificação que execute um CRC no corpo para garantir que ele foi transmitido corretamente.

(Curiosamente, tipos de esquema XML oferecem variabilidade negativa em suas definições de validação de esquema, algo que não predominante idioma ainda ofertas de programação, que é uma das maneiras em que a definição de esquema XML possível incompatibilidade em relação a linguagens de programação. Se isso se tornará um recurso disponível em qualquer linguagem de programação como-mas-unwritten e se ele seria um bom Thing To Have, é uma discussão interessante melhor tinham ao longo de cerveja.)

Em muitos sistemas, variação negativa geralmente é tratada usando construções de código explícito no nível do cliente — ou seja, é para os usuários do tipo de mensagem fazer alguns tipos de if/else é de teste para ver qual tipo de mensagem antes de examinar o corpo, que processa o trabalho, coloque a família de mensagem quase irrelevante. Há muita variabilidade negativa o design de saída costuma ser a causa de chamadas de desenvolvedores para a “ com densidade tudo e recomeçar. ”

Compatibilidade de ligação e variabilidade

No momento em que reais que aspectos comuns e variabilidade são definidos varia de acordo com cada paradigma e, em geral, quanto mais próximo seja executado de hora em que podemos fazer a ligação essas decisões, mais controle fornecemos aos clientes e os usuários sobre a evolução do sistema e como um todo. Ao abordar um paradigma específica ou uma técnica dentro de um paradigma, é importante reconhecer no qual um desses quatro “ bind vezes ” a variabilidade entra em ação:

  1. Fonte de tempo. Este é o tempo antes que o compilador aciona acima, quando o desenvolvedor (ou alguma outra entidade) é criar os arquivos de origem que eventualmente irá ser alimentado no compilador. Técnicas generative do código, como, por exemplo, o mecanismo de modelo T4 — e a um grau menor do sistema asp.NET — operam em uma vinculação de fonte de tempo.
  2. Tempo de compilação. Como o nome sugere, essa ligação ocorre durante a passagem do compilador sobre o código-fonte para renderizá-lo em código de bytes compilado ou instruções executáveis de CPU. Uma grande quantidade de tomada de decisões é finalizada aqui, embora nem todos do mesmo, como veremos.
  3. Tempo de link/carregar. Entra no momento em que o programa carrega e executa, um ponto adicional de variabilidade em ação, com base nos módulos (assemblies, no caso do .net; específicosAs DLLs, no caso de código nativo do Windows) que são carregados. Isso é comumente conhecido como uma arquitetura de plug - in ou adicionar-em-style quando ela for aplicada em um nível de escala de todo programa.
  4. Tempo de execução. Durante a execução do programa, determinados variabilities sejam capturadas com base na entrada do usuário e decisão tomado e potencialmente diferente de código executada (ou até mesmo gerado) com base naqueles/entrada de decisões.

Em alguns casos, o processo de design será deseja começar estes “ vincular vezes ” e trabalho com versões anteriores para descobrir construções de linguagem que pode oferecer suporte a necessidade;Por exemplo, um usuário pode querer ter a capacidade de adicionar/remover/modificar variabilidade em execução o tempo (de modo que não precisamos voltar um ciclo de compilação ou recarregar o código), que significa que qualquer paradigma usa o designer, ele deve oferecer suporte a uma vinculação de variabilidade em tempo de execução.

Desafio

Em meu artigo anterior, eu deixei leitores com uma pergunta:

Como exercício, considere o seguinte: O .NET Framework 2. 0 introduziu os genéricos (tipos parametrizados). Por quê? Da perspectiva do design, o que a objetivo servem? (E para o registro, as respostas do “ IT nos permite ter coleções fortemente tipada ” não têm o ponto — o Windows Communication Foundation usa genéricos amplamente, claramente de maneiras que não são coleções de trata apenas de tipo seguro.)

Levando isso um pouco mais, examine a implementação (parcial) de uma classe de pontos em 1 Figura , que representa um cartesiano X / Y ponto, como o pixel coordenadas em uma tela ou em um gráfico mais clássico.

A Figura 1 a implementação parcial de uma classe de ponto

Public Class Point
  Public Sub New(ByVal XX As Integer, ByVal YY As Integer)
    Me.X = XX
    Me.y = YY
  End Sub

  Public Property X() As Integer
  Public Property y() As Integer

  Public Function Distance(ByVal other As Point) As Double
    Dim XDiff = Me.X - other.X
    Dim YDiff = Me.y - other.y
    Return System.Math.Sqrt((XDiff * XDiff) + (YDiff * YDiff))
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    ' Are these the same type?
If Me.GetType() = obj.GetType() Then
      Dim other As Point = obj
      Return other.X = Me.X And other.y = Me.y
    End If
    Return False
  End Function

  Public Overrides Function ToString() As String
    Return String.Format("({0},{1}", Me.X, Me.y)
  End Function

End Class

No e ele mesmo, não é realmente tudo o que é interessante. O restante da implementação é da esquerda para a imaginação do leitor, porque ele não é fundamental para a discussão.

Observe que essa implementação ponto fez algumas suposições sobre a forma como pontos devem ser utilizados. Por exemplo, os elementos de X e Y do ponto de são inteiros, que significa que essa classe ponto não pode representar pontos fracionários, tais como pontos de (0.5,0.5). Inicialmente, isso pode ser uma decisão aceitável, mas inevitavelmente, uma solicitação será exibida pedindo para poder representar “ pontos fracionários ” (por algum motivo). Agora, o desenvolvedor tem um problema interessante: Como representar deste novo requisito?

A partir das noções básicas, let’s farão a coisa “ Lord AH não faça isso ” simplesmente criar uma nova classe de pontos que usa os membros de ponto flutuante, em vez de membros integrais e ver o que surge (veja a Figura 2 ;Observe que PointD é a abreviação de “ ponto-duplo, ” significando que ele usa doubles). Como limpar bem, existe muita sobreposição conceitual aqui entre os dois tipos de ponto. De acordo com a teoria a semelhança/variabilidade do design, isso significa que precisamos para capturar as partes comuns e permitem a variabilidade de alguma forma. Orientação a objetos clássica teria que nós fazê-lo por meio de herança, elevando o comum em uma classe base ou interface (ponto), depois que a implementação em subclasses (PointI e PointD, talvez).

De de Uma nova classe de pontos com membros de ponto flutuante, a Figura 2

Public Class PointD
  Public Sub New(ByVal XX As Double, ByVal YY As Double)
    Me.X = XX
    Me.y = YY
  End Sub

  Public Property X() As Double
  Public Property y() As Double

  Public Function Distance(ByVal other As Point) As Double
    Dim XDiff = Me.X - other.X
    Dim YDiff = Me.y - other.y
    Return System.Math.Sqrt((XDiff * XDiff) + (YDiff * YDiff))
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    ' Are these the same type?
If Me.GetType() = obj.GetType() Then
      Dim other As PointD = obj
      Return other.X = Me.X And other.y = Me.y
    End If
    Return False
  End Function

  Public Overrides Function ToString() As String
    Return String.Format("({0},{1}", Me.X, Me.y)
  End Function

End Class

Problemas interessantes surgem tentem isso, no entanto. Primeiro, as propriedades X e Y precisam de um tipo associado a eles, mas a variabilidade entre as duas preocupações de subclasses diferentes como as coordenadas X e Y são armazenadas e, portanto, representadas aos usuários. O designer sempre simplesmente poderia optar por representação a maior/a mais ampla/mais abrangente, que, nesse caso seria um Double, mas agora fazer positivo significa ter um ponto que só pode ter valores inteiros está perdido, como uma opção e ela desfaz todo o trabalho a herança foi projetada para permitir. Além disso, porque elas são relacionadas pela herança agora, as duas implementações de herança de ponto de agora são supostamente intercambiáveis, portanto, nós deve ser capazes de passar um PointD para um método de distância PointI, que pode ou não ser desejável. E é um PointD de (0. 0, 0) equivalente (como em é igual A) a um PointI de (0,0)? Todas essas questões devem ser consideradas.

Mesmo que esses problemas são alguma forma fixa ou feitos manejável, surgem outros problemas. Posteriormente, esperamos que um ponto que aceita valores maiores do que podem ser mantidas em um Integer. Ou valores positivos apenas absoluto (o que significa que a origem está no canto inferior esquerdo) são considerados aceitáveis. Significa que cada um desses requisitos diferentes devem ser criadas novas subclasses de ponto.

Revisão volta por alguns instantes, o desejo original era reutilizar a semelhança de implementação do ponto, mas permitir a variabilidade de representação/tipo dos valores que compõem o ponto. Em ideal, dependendo do tipo de gráfico que estamos trabalhando com, deve ser capaz de escolher a representação no momento em que o ponto é criado e isso representaria próprio como um tipo totalmente distinto e diferente, que é exatamente o que fazem os genéricos.

No entanto, fazendo isso, representa um problema: O compilador insiste “ representante ” tipos não tem necessariamente “ + ” e operadores “-” definidos para ele, pois ele considera que queremos colocar qualquer tipo de possível aqui — números inteiros, longos, seqüências de caracteres, botões, DatabaseConnections ou qualquer outro vem à mente — e isso é claramente um pouco demais variável. Dessa forma, uma vez, é necessário expressar a alguns aspectos comuns para o tipo que possa ser usado aqui, na forma de uma restrição de genérica em quais tipos “ representante ” pode ser (consulte do Figura 3).

De de uma restrição de genérica do tipo, a Figura 3

Public Class GPoint(Of Rep As {IComparable, IConvertible})
  Public Sub New(ByVal XX As Rep, ByVal YY As Rep)
    Me.X = XX
    Me.Y = YY
  End Sub

  Public Property X() As Rep
  Public Property Y() As Rep

  Public Function Distance(ByVal other As GPoint(Of Rep)) As Double
    Dim XDiff = (Me.X.ToDouble(Nothing)) - (other.X.ToDouble(Nothing))
    Dim YDiff = (Me.Y.ToDouble(Nothing)) - (other.Y.ToDouble(Nothing))
    Return System.Math.Sqrt((XDiff * XDiff) + (YDiff * YDiff))
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    ' Are these the same type?
If Me.GetType() = obj.GetType() Then
      Dim other As GPoint(Of Rep) = obj
      Return (other.X.CompareTo(Me.X) = 0) And (other.y.CompareTo(Me.Y) = 0)
    End If
    Return False
  End Function

  Public Overrides Function ToString() As String
    Return String.Format("({0},{1}", Me.X, Me.Y)
  End Function

End Class

Nesse caso, duas restrições são impostas: um para garantir que qualquer tipo de “ representante ” pode ser convertido para valores double (para calcular a distância entre os dois pontos) e outro para garantir que os valores de Y e constituinte X podem ser comparados para ver se elas são maiores que/igual-para/de menor-que uma da outra.

E agora o motivo de genéricos se torna mais claro: Eles oferecem suporte a um diferente “ eixo ” de variabilidade de design, o que é totalmente diferente do eixo com base em herança tradicional. Ele permite que o designer processar a implementação como semelhanças e os tipos que está sendo operados pela implementação ser variabilities.

Observe que essa implementação pressupõe que a variabilidade está ocorrendo no momento da compilação, em vez de em tempo de carga/link ou tempo de execução — se o usuário quer ou precisa especificar o tipo de no X / Y membros o ponto no tempo de execução, em seguida, outra solução é necessária.

Não inativo (ou concluído) ainda!

Se todo o design de software é um exercício gigante nos aspectos comuns e variabilidade, fica clara a necessidade de entender multiparadigmatic design: Cada um dos paradigmas diferentes oferece diferentes maneiras de se alcançar essa semelhança/variabilidade e mixagem de paradigmas cria confusão e leva a uma chamada para uma reescrita completa. Assim como o cérebro humano começa a ficar confuso quando tentamos mapear construções tridimensionais em nossa head em quatro e cinco dimensões, dimensões de excesso de variabilidade em software causam confusão.

Nos artigos a seguir half-dozen ou então, vai ser observando as diferentes maneiras que cada paradigma suportada pelo c# e Visual Basic — os paradigmas estruturais, orientado a objeto, metaprogramming, funcionais e dinâmicos, sendo os principais elementos — oferecem uma funcionalidade para capturar os aspectos comuns e permite a variabilidade. Quando estamos por meio de tudo isso, examinaremos como alguns deles podem ser combinados de maneiras interessantes para tornar seus designs mais coisas modular, extensível, passível de manutenção e tudo o que outros.

Codificação feliz!

Ted Neward  é uma entidade de segurança com Neward & Associa uma empresa independente especializado em sistemas da plataforma Java e empresa do .NET Framework. Ele escreveu mais de 100 artigos, é um palestrante da INETA e de MVP de c# e tem autor e co-autor de livros de uma dúzia, incluindo “ Professional F # 2. 0 ” (Wrox 2010). Além disso, ele consulta e mentors regularmente. Entrar em ted@tedneward.com de com perguntas ou solicitações de consultoria e leia seu blog em blogs.tedneward.comde .

Meus agradecimentos aos seguinte especialista técnico para revisar este artigo: Anthony verde