4D 20 R5 oferece uma caraterística poderosa para desenvolvedores: Singletons!
O padrão de design singleton cria uma única instância de uma classe acessível em toda a sua aplicação.
Esse padrão oferece muitos benefícios, incluindo:
- host para valores entre processos,
- classes utilitárias,
- uma base para o padrão de projeto fábrica,
- e muito mais.
Continue lendo para mais informações sobre esse novo conceito!
4D tem 2 tipos de singletons: singletons que são únicos por processo e singletons compartilhados, que são únicos por toda a aplicação. Singletons são objetos padrão, enquanto singletons compartilhados são objetos compartilhados.
Definir um singleton é extremamente fácil; basta adicionar a palavra-chave singleton a um construtor de classe:
singleton Class constructor()
Definir um singleton partilhado também é muito fácil; basta utilizar a palavra-chave singleton numa classe partilhada:
shared singleton Class constructor()
A partir desse momento, pode acessar facilmente aos seus singletons através do atributo me:
$singleton:=cs.SingletonClass.me
O atributo me é a instância do singleton (frequentemente acessada através da função getInstance() noutras linguagens). É instanciado na primeira vez que acesse seu singleton.
Exemplos
Os singletons são muito úteis, e esperamos que comece a usá-los muito. Penso que prefere exemplos de singletons em vez de uma longa descrição, por isso preparei alguns.
Classe utilitária
Vamos começar com um exemplo simples: a classe utilitária. A classe utility aloja muitas funções utilitárias que são utilizadas em todo o lado na sua aplicação. Agrupar estas funções numa única classe ajuda a manter o seu código, uma vez que é muito fácil encontrar todas as suas funções utilitárias e estas não sobrecarregam os seus métodos.
//Classe UtilityClass
singleton Class constructor()
Function utilityFunction1()
Function utilityFunction2()
...
A partir desse momento, pode chamar as suas funções utilitárias com esta linha:
cs.UtilityClass.me.utilityFunction1()
Wrapper do servidor Websocket
Outra utilização clássica dos singletons é criar invólucros para objetos que devem estar acessíveis em qualquer parte da sua aplicação, como um servidor de websocket. Vamos considerar esta classe:
//WebSocketServerWrapper
singleton Class constructor()
$handler:=cs.myWebsocketServerHandler.new()
This.webSocketServer:=4D.WebSocketServer.new($handler; {dataType: “object”})
Function terminate()
This.webSocketServer.terminate()
A partir desse momento, pode iniciar o seu servidor Web chamando::
CALL WORKER(“WebSocketServerWorker”; Formula(cs.WebSocketServerWrapper.me))
E encerrá-lo chamando:
CALL WORKER(“WebSocketServerWorker”; Formula(cs.WebSocketServerWrapper.me.terminate()))
Factory
O terceiro exemplo implementa um padrão de design: a fábrica. A fábrica é responsável por criar novas instâncias de objetos, neste caso, objetos Veículo provenientes de uma VehicleFactory:
//Classe VehicleFactory
property vehicleBuilt:=0 //Número de veículos construídos pela fábrica
shared singleton Class constructor()
shared Function buildVehicle($type : Text)->$vehicle : cs.Vehicle
Case of
: $type="car"
$vehicle:=cs.Car.new()
: $type="truck"
$vehicle:=cs.Truck.new()
: $type="sport car"
$vehicle:=cs.SportCar.new()
: $type="motorbike"
$vehicle:=cs.Motorbike.new()
Else
$vehicle:=cs.Car.new()
End case
This.vehicleBuilt+=1
Graças ao fato de ser um Singleton, pode chamar esta VehicleFactory para obter um novo Veículo a partir de qualquer ponto do seu processo (ou aplicação, se o tornar partilhado) com uma única linha:
$vehicle:=cs.VehicleFactory.me.buildVehicle("truck")
Nesse caso, esta fábrica não faz muito para além de contar o número de veículos construídos antes de devolver o seu novo veículo, mas penso que já percebeu a ideia.
Se quiser que a VehicleFactory seja única em toda a sua aplicação, tem de lhe acrescentar a palavra-chave shared. A função buildVehicle modifica a VehicleFactory (incrementando This.vehicleBuilt), pelo que também é necessário adicionar-lhe a palavra-chave shared para que use e end use sejam chamadas automaticamente ao chamar e sair da função.
Valores entre processos
Outra utilização clássica dos singletons é alojar valores interprocessuais que serão utilizados em toda a aplicação. Aqui está um exemplo de um singleton desse tipo:
//Classe InterprocessSingleton
shared singleton Class constructor()
This.version:=2
This.buildNumber:=1056
A partir daí, você pode acessar suas variáveis globais muito facilmente, por exemplo:
cs.InterprocessSingleton.me.version
Dica: Por vezes, pretende inicializar os valores interprocessos no arranque da aplicação, normalmente dentro do método On Application Startup e, nesse caso, poderá ter de passar parâmetros para o construtor do singleton, como no exemplo abaixo:
//Classe InterprocessSingleton
shared singleton Class constructor($file : 4D.File)
$json:=$file.getText()
$settingsJson:=JSON Parse($json)
This.version:=$settingsJson.version
This.buildNumber:=$settingsJson.buildNumber
Nesse caso, os valores de version e buildNumber vêm de um arquivo JSON. No caso de singletons, há uma maneira de chamar o construtor com parâmetros chamando a função new(). Por exemplo, no nosso caso:
$myFile:=File("/RESOURCES/settings.json")
cs.InterprocessSingleton.new($myFile)
É preciso ter cuidado para que a chamada a new() seja a primeira vez que o singleton é acessado. Chamar new() depois de o singleton ter sido acessado já não chamará o construtor da classe.
Agradecimentos especiais
Quero fazer um agradecimento especial a Chris Belanger, que sugeriu adicionar o atributo me para aceder a um singleton.
Espero que os singletons sejam tão interessantes para si como são para nós. Se tiver alguma questão sobre eles, sinta-se à vontade para perguntar no fórum 4D.