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