Pretendo manter a interface pública das classes o mais parecida possível entre todas as linguagens, a não ser que isso viole de forma muito óbvia alguma característica de cada linguagem.
Logo depois de publicar a postagem, me dei conta de alguns esquecimentos, algumas inadequações, e algums escolhas de design "suspeitas", de forma que, alguma reflexão e alguma pesquisa depois, venho apresentar as classes refatoradas.
Em primeiro lugar, movi o código para o GitHub, de forma que o leitor sempre terá onde encontrar o código atualizado, bem como o histórico de modificações. As postagens antigas não vão ser editadas para refletir as mudanças que forem ocorrendo, mas as mudanças mais significativas serão discutidas em postagens específicas, se for o caso.
Para este refactoring, as mudanças de API (que impactam a implementação de ambas as linguagens) foram as seguintes:
- Implementação (conforme previamente modelado) do valor nulo para elevação, permitindo que haja um construtor com dois argumentos (latitude e longitude, nessa ordem), e um argumento opcional elevação cujo valor padrão é nulo;
- Eliminação do construtor que usava uma sequência como argumento, pois isso enfraquecia a semântica, e dava margem a erros lógicos (pois não havia garantia de que os itens da sequência viriam na ordem correta), e de tempo de execução. Agora, a classe possui apenas um construtor, e esse construtor recebe dois argumentos numéricos, ou três argumentos numéricos. Uma discussão sobre essa decisão pode ser vista nesta pergunta do StackOverflow;
- Apenas a Elevação possui setter. Latitude e a Longitude são somente leitura (e portanto possuem apenas getter);
Versão atual em C#:
using System; using System.Collections.Generic; using System.Linq; namespace GpsDataModel { public struct Position { private readonly double _lat, _lon; private double? _elev; public Position(double lat, double lon, double? elev = null) : this() { _lat = Math.Abs(((lat-90) % 360) - 180) - 90; _lon = ((lon-180) % 360) - 180; _elev = validateElevation(elev); } public double Latitude { get { return _lat; } } public double Longitude { get { return _lon; } } public double? Elevation { get { return _elev; } set { _elev = validateElevation(value); } } private double? validateElevation(double? elev) { if (elev < -WGS84.SEMI_MAJOR_AXIS) throw new ArgumentOutOfRangeException( String.Format("Elevation {0:0.00} " + "is deeper than the center of the Earth!", elev)); else return elev; } public override string ToString() { return String.Format("Position({0:0.000}, {1:0.000}, {2:0.0})", _lat, _lon, _elev); } } }
Comentários específicos sobre a implementação C#:
- O argumento opcional é apresentado com um valor padrão, representado sintaticamente pelo operador "=";
- Os tipos numéricos nullable são sufixados por um ponto de interrogação - no exemplo, "double?";
- A lógica de validação do setter da Elevação, que deve ser usada também no construtor, foi movida para a função validateElevation;
Versão atual em Python:
#!/usr/bin/env python # coding: utf-8 import WGS84 class Position(object): __slots__ = ('_lat', '_lon', '_elev') def __init__ (self, lat, lon, elev=None): self._lat = abs(((float(lat)-90) % 360) - 180) - 90 self._lon = ((float(lon)-180) % 360) - 180 self.elevation = elev @property def latitude(self): return self._lat @property def longitude(self): return self._lon @property def elevation(self): return self._elev @elevation.setter def elevation(self, elev): if elev > (-WGS84.SEMI_MAJOR_AXIS): self._elev = float(elev) else: raise ValueError ("Elevation %.2f " + " is deeper than the center of the Earth!" % elev) def __str__ (self): return ("Position(%.3f, %.3f, %.1f)" % (self._lat, self._lon, self._elev))
Nenhum comentário:
Postar um comentário